login

Solution

這題是考 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) 取得 username。
乍看之下是剛好,但是 scanf("%s") 的特性會在字串結尾補上 \00,因此會有 off-by-one 的問題。
因此只要輸入長度 256 的 username 即可讓身份變為 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}