攻防CTF初体验
半个月前在某位可爱的计安SA的组织下,go第一次体验了 Attack/Defense CTF。虽然因为各种原因只体验了几个小时 ,但也感受到了CTF中绞尽脑汁发掘漏洞进行攻防的魅力。
这次CTF长达72小时,一共有5道题。每位同学都有5台靶机分别部署着5个存在漏洞的服务,攻击方的目标是通过漏洞读取 /var/flag.txt
中的 flag,而防守方的目标则是在保证服务正常运行的同时修复漏洞避免泄露 flag。比赛5分钟一个轮次,每轮中所有 flag 都会刷新,会根据攻击成功和防守成功的人数发放分数。
第一题是道签到题,是一个用于输出字符串长度的PHP服务。
1 |
|
代码非常简单,中间存在一个 eval
很明显就是漏洞,把这行改成没有 eval
的形式即可修复。
1 | print "$str length is $str_len"; |
攻击则可以注入一个这样的字符串
1 | '.shell_exec("cat /var/flag.txt").' |
1 | print eval("print ''.shell_exec("cat /var/flag.txt").' length is $str_len';"); |
第二题仍然是一道PHP服务,提供了一个计算器的功能,其中核心部分如下
1 |
|
这里我们可以发现一个 shell_exec
,因此思路可以是用类似SQL注入的方式,拼接一个命令上去执行。
怎么防御非常好想到,echo "$str"
无论怎样在 $str
中存在一个双引号,才可以将中间的部分作为命令执行,而正常的计算应该根本不会出现双引号这种字符,因此可直接过滤双引号。
1 | if(strpos($str, "\"") === false){ |
我在这里亲切地给了一个 “error” 作为攻击失败的提醒,而有些同学则不讲武德
另外值得一提的是,bc
这个命令本身支持字符输入的,例如 echo "a=1;b=2;a+b" | bc
是能产生正常输出的。看到有些同学尝试在过滤字符甚至 “cat” ,导致没通过正常输入校验。
然而,这道题的攻击我思考了相当长的时间。拼接的字符串以 | bc
结尾,标准输出流应该会被 bc
接收到,因此首先尝试绕开 bc
。在这里我想到了可以用 &&
进行短路,插入一条失败命令导致 bc
无法执行,因此尝试拼接这样一个字符串
1 | echo "x" | cat /var/flag.txt && rm / | bc |
随后发现,shell_exec
这个函数在 shell 错误退出时返回 null
,构造这样的输入根本拿不到结果。随后我开始考虑不绕开 bc
,而是将 bc
作为正常输出的最后一步。由于bc
是个计算器,因此 bc
的输出应该是一个数字,如果我能把 flag 编码成一个数字,拿到输出结果后进行解码即可获得 flag。
随后我查询了下 flag 的格式 ,为 flag{中间有32个16进制字符}
,flag中间的部分碰巧正好是16进制的,我只需要获取这16进制表达的数字即可。在这个思路下,我构造出了这样一个复杂的输入
1 | echo "x" | cat /var/flag.txt | grep -o -E '[0-9a-f]{20,}' | awk '{print "ibase=16; "toupper($1)}' | bc |
前几天还在 channel 上吐槽 shell 的文本处理不好用,结果学了两天就碰巧用上了😂
这句话大意是 cat
获取文件内容,然后用 gerp -o
以正则表达式提取中间的16进制部分,使用 awk
将16进制转大写,最后把 ibase=16; ABCDEF0123456789
这样的内容作为 bc
的标准输入,bc
会把 ABCDEF0123456789
当成一个16进制数,并输出它的10进制格式。
当时构造出来就觉得这输入太复杂了,应该有更简单的,不过确实能用
构造出这个输入时比赛恰好开始了两个小时,在榜上看到我是第二个构造出第二题攻击方式的同学,非常激动。
比赛后跟其他同学询问了一圈,发现我把绕开 bc
这个事想的太复杂了,可以直接用注释等好多方法搞定,当时完全没想到。
1 | echo "x" | cat /var/flag.txt # | bc |
做出第二题之后,因为各种原因没法继续参加比赛了,但这次攻防确实非常有趣。非常感觉SA,在主机和网络一次次崩溃的情况下 为我们提供了这样一个比赛机会。