Wednesday, September 24, 2014

Monday, September 22, 2014

CSAW 2014 Exploitation 400 - Saturn

[Participated with Segfault]
You have stolen the checking program for the CSAW Challenge-Response-Authentication-Protocol system. Unfortunately you forgot to grab the challenge-response keygen algorithm (libchallengeresponse.so). Can you still manage to bypass the secure system and read the flag?
The binary is an ELF 32-bit binary that accepted socket accepted input from stdin(and was running using xinetd or something similar). Looking at the binary we can see that there is a call to _fillChallengeResponse from the missing shared object file, that is probably setting two global variables(renamed as g1 and g2). 

The program then enters the following loop, where it reads a byte from the user, ands it against 0xF0, compares it against 0xA0, 0xE0 and 0x80 -- and performs actions accordingly.
Case 160:
We can see that our input char is and'd with 0x0F, left shifted twice(multiply by 4), added to the base address of g2 and 4 bytes are read back. Essentially, given an index, 4 bytes can be dereferenced starting at an offset from g2.
Case 128:
This function is interesting as it computes the computes the product of the contents of a global integer array and prints out the flag if the product is 1. Needless to say, each element of the global array is initialised to 0.
Case 224:
The input character is and'd with 0xF, left shifted by 2 and used as an index to deref addresses with g1 as the base. It then reads 4 bytes from the user and checks its contents against the deref address; if they match they set the globalarray[pos] = 1 where pos is calculated as inputchar & 0xF.
Armed with this information, we can try and write a python script that supplies values to trigger case 160, leaks values, triggers case 224, uses the leaked value to set a position in the global array to 1. Finally, we can trigger case 128 to read the key.
One could create a libchallengeresponse.so file with a _fillChallengeResponse function that could set g1 and g2, so that we could try debugging the saturn binary against our exploit.
The exploit can be found here.

Friday, July 11, 2014

Hopper scripting bug : Document.getCurrentDocument().getSegmentAtAddress(addr)

I like using Hopper; however, there do seem to be a few bugs when scripting that require working around. One such bug I encountered recently is the instance where the function `Document.getCurrentDocument().getSegmentAtAddress(addr)` returns None for a perfectly valid address. This behaviour occurs when you have a loop and you call the function repeatedly from within it(yeah I probably shouldn't do that but well...).

The workaround was to do something as follows :-

curseg = Document.getCurrentDocument().getSegmentAtAddress(addr)
while curseg is None:
time.sleep(2)
curseg = Document.getCurrentDocument().getSegmentAtAddress(addr)


Ugly but works!

Monday, July 7, 2014

pwnium 2014 rev300 (using Hopper scripting)

[Played with Team Segfault]
I've noted down a failed approach and a solution in the form of a Hopper script that I came up with after the CTF.
teammate who had solved the challenge in time has written a writeup on the same here(using PIN). Other writeups for the challenge can be found here(static analysis), and here(using gdb tracing). I strongly urge you to read those interesting writeups before proceeding.

-===========================]
Failed attempt description
During the CTF, I was trying to solve the problem in a bottom up fashion rather than top down. I realised that there were 6 possible end points that moved "1" into rax before returning from the function, something that was required to reach the winning state of the crackme. An instance is shown below. It is clear that one way of reaching that instruction would be if "al" evaluated to "0x6f".
As there is an unconditional jmp at 403574, the only way to get to 403580(the cmp instruction) would be via 403579(mov rax, qword [ss:rbp-0x0+var_m8]). So lets examine XREFS at this point,
Visiting the XREF we have,

Hence, to get to this point, eax needs to have a value of 0x90305228. Scrolling up we can see that eax gets the value here :-
Now we could examine the XREFS for the dword at 0x606b18 and see where its set to 0x90305228.

So far so good. We can continue tracing XREFS in this manner, however, this strategy fails to work for two reasons :-

  1. There are self loops.
  2. At certain points you see that the state value is set at multiple locations as shown in the image below.

-===========================]
Working approach

A hopper script that automates a top down approach can be found here. It uses the initial state value and follows checks as the occur, keeping track of state values. The code is simple, for API reference I used HopperScripts.

Tuesday, May 27, 2014

Playing around with getcwd, readpath and readlink

Recently, I had to write some code that dealt with the functions getcwd, readpath and readlink and I noticed a few peculiarities in them. I performed a few small tests and below are my observations(this post is mostly so that I can document for later reference if needed).

A few notes based on what the man pages say.

-=========================]
char *getcwd(char *buf, size_t size);
  1. Return a null terminated string containing an absolute pathname as the cwd
  2. Size should be the length of buf
  3. If length of the cwd > size of buf, NULL is returned, errno is set to ERANGE. An application should check for this error and allocate larger buffers if necessary.
  4. On success, it returns a pointer to buf. 
-=========================]
char *realpath(const char *path, char *resolved_path);
  1. Return a null terminated string, upto a maximum of PATH_MAX bytes in `resolved_path`
  2. if `resolved_path` is NULL, realpath allocates a buffer of upto PATH_MAX bytes and returns a pointer
  3. If function fails to resolve the path, NULL is returned. 
-=========================]
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
  1. Contents of the symbolic link are placed in `buf`, of size `bufsize`.
  2. A null byte is not appended into buf
  3. If the buffer is too small to hold the contents, the results are truncated to bufsize.
-=========================]
A few silly tests

Lets try and create a few 1000 nested directories such that the length of the cwd exceeds 4096. I used this bash script. Interestingly the `pwd` at the end of the script works and prints out a path of length 7022.

We can't cd or ls that directory from home -- we get an error File name too long.

In order to cd into the directory we could use something as :-
for i in `seq 1 1000`; do cd tester; done

Lets try using readlink to resolve /proc/self/cwd using this. Running the code resulted in a "File name too long" error.

Trying to use getcwd to print the current working directory using this, surprisingly works.

Using realpath to resolve the full path of a file in the current directory as shown here, I get an error message Numerical result out of range.

Sunday, April 13, 2014

PlaidCTF pwnable 200 ezhp

[Played with Team Segfault]

The challenge provided a means for adding, removing, changing and printing of notes. The challenge binary was not made available for a while initially -- playing around with the remote challenge for a while we can notice that a couple of crashes happens while removing notes.

On obtaining the binary, we can RE it to find the following interesting functions. NX is disabled, unsure about ASLR.

-========]
Analysis

The "add" function checks if the number of notes has exceeded 1022, if not allocates memory for a new note. The size of the note depends on user input. Even though the allocation happens based on a size given by the user, the size is not kept track of in a variable or struct. Memory allocation does not seem to be using malloc, its something custom made(I've named it "allocate_space" below). The code for allocation looks a bit more involved, maybe I'll come back to that later if needed.


The "change" function takes in a size, and reads input to an earlier allocated note. Needless to say, we don't keep track of the allocated sizes, and can cause reads that read beyond the allocated space. Nice.
The "remove" notes function looks normal too, it has sanity checks in place. However, it seems to be calling what seems like a custom made "free"(I'm calling it "free_space"). Its clear that we can control writes to arbitrary memory by manipulating next and prev. 
From the above code, we can deduce that the "metadata" of each allocated "block" starts 12 bytes before the actual block. At 4 bytes and 8 bytes from the start of the metadata we have 2 pointers. If we can overwrite one pointer with the GOT address of puts(minus 4) and the second with the address of our shellcode, we could get a shell when "puts" is called next.
Looking at the "print" notes function we can see that, the data is printed out using a puts. 

-========]
Debugging

Creating 3 notes and debugging in gdb, we get the following memory layout.

We can confirm a few things :-
- It is possible to overwrite the block metadata of the next block and overwrite pointers as needed. I could create 3 blocks, the third containing the shellcode, the first overwriting the metadata of the second. I could then free the second to overwrite pointers.
- We can have an infoleak when puts tries to print notes. This way we can get the address of where the next block ends. In the figure above, after the block of "B"'s we have the address 0x0804c342(its actually 0x0804c300, I just overwrote the LSB of the address, more on this later). 0x0804c300 is the address where the next block(the last block of nops) ends.

-========]
Exploitation

The final exploit for the challenge can be found here
-========]
Note :
The leaked address might contain a "\x00" as the LSB, hence it is important to overwrite it with a "B"(as shown in the figure under "Debugging") so that "puts" leaks the address properly.

Saturday, April 5, 2014

Playing around with SROP

Recently, an interesting paper came out[1] that described an interesting mechanism that could be used for the exploitation of memory corruption vulnerabilities. While the paper does a good job of explaining how such an attack could be performed, this is my attempt at reproducing the same with a PoC and introducing a library that could be used to write such exploits in an easier fashion. This paper was interesting for two reasons : it talked about "portable shellcode" and "relative ease of exploitation".

-============================]
Vulnerable code

The code can be viewed here[2]. It contains an obvious overflow, is compiled statically, and has inline assembly to make the demonstration even easier. The server code also has intentional info leaks where it gives out information about a the address of a local buffer and the address of a page mapped in using mmap, that has RW permissions. The PoC exploit for 32 bit does not use the mmap'd region and is run on non-ASLR systems.


-============================]
Concept

Signal handlers return to a stub that perform a system call named "sigreturn". Its purpose is to restore the execution state of the program that had been interrupted when the signal handler was called. It does this by popping a signal frame that contains the values of all registers and storing those values into the registers.

This presents us with an opportunity to craft fake signal frames and perform a kind of "SROP-chaining".

Assumptions made :-
[a] EAX/RAX has the register value of 0x77/0xf.
[b] The "syscall/int 0x80 instruction is present"
[c] There is a writeable page in memory whose address we know.
[d] Nulls are allowed in the payload.

Advantage of using SROP the authors[1] point out is that :-
- You only need the address of one gadget "syscall/int 0x80; ret".
- For the most part, the signal frames that are created can be reused.

-============================]
Exploitation(32-bit)

Let us consider the vulnerable program shown at [2]. An EIP overwrite can be achieved by giving in input of length greater than the buffer size. We can notice that for ease of presenting the idea, we have added an inline gadget to meet requirement [a][b], and a call to mmap to accurately meet [c]. The input is read in the vulnerable program using "read" to circumvent the null space terminal requirement and meet [d].

The exploit can be viewed here[3]. It performs a jump to the gadget that performs a sigreturn call which in turn will load the sigreturn frame from the stack and load it into the registers. In order to ease the creation of the signal frame you could choose to do use the Frame module[4] like the following :-
>>> from Frame import SigreturnFrame
>>> frame = SigreturnFrame()
>>> frame.setvalue("<register>", <value>)
>>> sploit += frame.get_frame()

An exploit that spawns an execve shell can be found at [3].

-============================]
Further plans
- Add support/poc for x64 , its been added[5], thanks for [Segfault]Reno for helping out.
- Add support/poc for ARM
- Try finding out global unvarying addresses for gadgets such as "int 80/syscall/svc; ret" across multiple distros.

References :-
[1] Framing Signals, A return to portable shellcode
[2] https://github.com/eQu1NoX/srop-poc/blob/master/poc-32.c
[3] https://github.com/eQu1NoX/srop-poc/blob/master/sploit-32.py
[4] https://github.com/eQu1NoX/srop-poc/blob/master/Frame.py
[5] https://github.com/eQu1NoX/srop-poc/blob/master/sploit-64.py

Sunday, March 23, 2014

Random diffing

So, this is how people get past the registration process of a text editor thats been gaining popularity lately.





Monday, March 17, 2014

RuCTF 2014 Vuln 300

[RuCTF 2014 with Segfault]

I did not solve this challenge on remote in time, only locally. The application "Posts" was a CLI application, statically compiled(buttload of gadgets, yes), had a NX stack. It asked you for a name, a count, and read in `count` number of "Titles" and "Contents".

If we give 260 A's as the content, a function pointer is overwritten and we have a nice crash.


Next step, setup a ROP payload and jump to it. It would be great if we could use Posts as the location but it had a '\r' in its address which would prevent that from working.

The final sploit involved setting up the ROP chain in `name` and overwriting the function pointer with the address of the ROP chain. Locally the buffer address was 0xbffff5f4.


RuCTF 2014 crypto 100

[RuCTF 2014 with Segfault]

The questions stated :-
Server (python27.quals.ructf.org:12337) accepts only authorized messages.
It works like this:------------------------------- buf = c.recv(4096) digest, msg = buf.split(" ", 1) if (digest == md5(password+msg).hexdigest()): #here I send a secret else: c.send("Wrong signature\n")-------------------------------

You have intercepted one authorized message: "b34c39b9e83f0e965cf392831b3d71b8 do test connection". Construct your own authorized message! Answer starts with 'RUCTF_'

It seemed pretty obvious that it was a hash length extension attack. I did not have any library to automate the attack so I ended up writing a bit of python to wrap around hash extender, a nice C library for performing hash length extension attacks.

We do not know the length of the length of the padding required and need to bruteforce that.

The solution for the same can be found here.