ddaa's blog
Write-ups for CTF.
ddaa's blog

0CTF 2015 Exploit 300 login

XCTF , off-by-one , fmt str vuln

Share Tweet Share

這題跟 yench 討論
至少省下我一個小時突破盲點 XD
上學期辛苦開程式安全終於感覺有回饋了 QQ


這題是考 format string vulnerability
執行程式要我們輸入帳密
打開 ida pro 很容易就知道帳密多少
輸入 guest / guest123 以後成功登入
接著有三個功能可以選擇:

== 0CTF Login System ==
1. Show Profile
2. Login as User
3. Logout
=======================
Your choice:

但其實還有一個隱藏功能 4
必須讓自己的身分變成 normal 才能觸發
功能 4 是登入成 root 的功能
裡面有兩個很明顯的 printf(buf)
所以首先我們要讓自己的身份從 guest 變成 normal
pseudo code 大概長這樣:

char user[256];
int64 mode;
...
if ( choice == 4 && !mode)
    root_login()

Login as User 利用 scanf("%256s", user) 取得 user
乍看之下是剛好 但是 scanf("%s") 的特性會在字串結尾補上 \00
因此會有 off-by-one 的問題
因此只要輸入長度 256 的 user 即可讓身份變為 normal

進入 root_login() 以後的行為如下:

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);

登入失敗會用 printf(user) 印出使用者名稱
一個非常明顯的 fmt str vuln
我一開始以為 binary 中的 0ops{secret md5} 是被替換掉的
真實環境會放真的 md5 ... 只要 leak 出密碼就過了
後來試一下才發現那個字串真的就是那樣 = =
md5 的結果是 hex string 的形式
所以不可能滿足條件 XD

由於最後會用 exit(1) 結束程式
沒辦法透過改 ret address 去控制程式
所以很直覺的會想改 exit() 的 GOT
但是寫完 exploit 才發現這題的 GOT 竟然是 read-only XDDD
不確定是因為 PIE 的緣故或是有開啟其他保護機制

乍看之下無技可施了
正當我在嘗試研究 memcpy() 內部的是不是有可以利用的同時
yench 提醒我能不能改 printf() 的 rbp 去控制程式
結果是沒辦法~ 因為 printf() 最後沒有 leave
但是這讓我想起去年 HITCON 的某一題
是利用 sprinf() 任意改值造成 overflow 的二次利用
果然這題也是類似的做法
由於實際發生 fmt str vuln 是在 printf 內部的 vprintf()
因此可以將 printf() 的 return address 給改掉

有兩次 printf() 可以利用
因此只要第一次 leak 出 libc base 以及 stack base
第二次可以做 rop 攻擊
改 ret 跳到 pop rdi 再到 system() 就拿到這題的 shell 了~

exploit: login.py

flag: 0ctf{login_success_and_welcome_back}


comments powered by Disqus