---
title: swsec110_hw11_writeup
date: 2022-01-13
tags: swsec
---
# swsec110 hw11 writeup
學號: 309553032
網頁版:
https://hackmd.io/@johnson08/rk7CuWont
本次作業主要分成以下部分:
- [leak libc address](#leak-libc-address)
- 減少`_IO_write_base`的方式利用 partial overwrite, libc自動補 1 byte `\x00`
- [hijack vtable](#hijack-vtable)
- 將合用的 one_gadget (offset 0xe6c7e) 塞入 fwrite 會使用到的、vtable entry指向的 `_IO_file_overflow`
- 雖然理論上會先call到`_IO_file_xsputn`,應該也可以覆蓋這邊,但不知為何會造成segmentation fault
- [remote buffer flush](#remote-buffer-flush)
- local 的程式在 remote 無法直接 work 的原因是 buffer 沒有 flush 掉;填滿 buffer 或者換行可以flush, 這邊採用前者
- [catch the flag](#catch-the-flag)
## leak libc address
### libc 位址資訊位於 created file structure
由於此題不像 labs 有直接給 libc functions 位址,也不像 pwn2 可以 free unsorted bin 以 leak main arena, 而是一開始就空空如也,但基於 UAF 的精神還是翻了一下各個 chunks
翻著翻著就看到了某個很像屬於 libc 的東西

檢視其 file structure

發現他是 stderr, 這呼應到上課所提到的,glibc會把所有 file structure 串起來,以便程式終止時 flush buffers
總之,想要 leak 的目標在這邊(雖然之後發現不是)
有個可控的 file structure, 又有 fwrite 可用之,看似滿足任意讀的架構,但仔細想了一下,heap的位址也沒有,即便可以任意讀,也無法指定到該 chunk
### hint: _IO_IS_APPENDING
由出題的hint谷歌此flag可以如何達到leak,找到以下關鍵:

(src: https://n0va-scy.github.io/2019/09/21/IO_FILE/)
trace code 以及理解的篇幅在此文章皆有提到。綜上,複寫 fp 須達到三件事:
- fileno: 1 (stdout)
- flags: 0x1800
- `_IO_write_base` 改小
至此,想法如下:由於不知道`_IO_write_base`位置,上述三件事無法透過一次的 overwrite 完成;須先進行一次 overwrite, 將較高位址的 fileno 覆蓋為 1, 再 call 一次 `fwrite(note_buf, 1, BUF_SIZE, fp);`還原看起來比較資訊有效的 io read/write pointers, 再透過 (hint) partial overwrite 將想要讀取的位址指定到想要讀的地方

至此,fileno成功地被覆蓋為1, 但再執行一次 fwrite() 後,發現本來想要的酷酷的 chain 當然也被覆蓋掉了,因為是在 fileno 前面啊...

不過,就算 chain 可以被保留,fp 在遞增一個 buffer size 之後已經到遙遠的下方了,看看此時如果照原本想法 overwrite 可以得到什麼:


雖然不知道`_IO_wfile_jumps`是什麼,但能夠幫助我得到 libc 就對了,就照原訂計畫去 overwrite
實驗幾次後,發現overwrite的數值後面會再被補 null byte, 那麼退而求其次,讓 null bytes 被補到 `_IO_write_base` 的 lowerbyte, 以此達到減小`_IO_write_base` 的目的



看到`_IO_write_base`位址,減去其elf symbol offset 即可得到 libc 位址!


## hijack vtable
目標是將 `_IO_file_overflow` 改成 one_gadget

位於 `_IO_file_jumps + 0x18`
在 lab2,3 中,我們透過 fread() 達到任意寫,而這邊是 fwrite 任意寫。
google fwrite 怎麼任意寫

(src: https://xz.aliyun.com/t/5853#toc-6)
好像突然想起 fwrite 是一件 trivial 的事情,只要控來源目的就可以達成啊。
是這樣嗎?

覆蓋 `_IO_write_ptr`, `_IO_write_end` 的話,其前面的資料也會被覆蓋掉,write的來源就不明了。但這邊還是先遵照谷哥指示:

這邊卻發現好像是可控的區域!? 但是不知道這是哪部分
找一下 file structure, 也無法看出原因

實測幾次能改到的地方,發現是 payload 起頭的字串當作來源,於是修改 payload 如下:


成功塞成 one_gadget 了!
然而執行到這邊,會發現仍然無法過,看來是 one_gadget 用錯,條件不符合

事實上如果三個 gadget 都直接硬塞,仍然沒有一個能 work,
那麼來仔細檢視一下以上條件,在程式到 segmentation fault 時, regs 裡面有什麼是可控的

第一個 gadget 指定 r12, r15 址或值須為 null, 查看 regs, r15 已經有 null, r12 是一堆字串A, 這代表把先前用A覆蓋的方式改成用`\x00`覆蓋,基本上應該不影響前面exploit, 且可以滿足這裡的條件:

成功拿到 shell!
## remote buffer flush
如果直接把local測試好的程式跑在 remote 上,會直接卡住:

我的第一個做法是將 process.interactive()塞在程式中的故處,看到底是那裡卡了

重複測試,發現在要 leak libc 的部分時不會卡住,但也沒有照預期的把 libc leak 出來,而是直接 loop 下一次選項清單

說來難以相信,我沒有先想到 buffer flush 的問題,而是一邊想著到底是刪小一邊狂按3下去直到libc leak出來...

後來想到上課提到,要將 buffer 內容輸出,1是掃到換行字元,2是buffer滿了會被flush, 既然我是重複 write() 直到 flush, 那就暴力的寫個回圈吧...

## catch the flag
