my blog
Log | Files | Refs | Feed (4983B)

# A simple vulnerability

Created: Fri Apr 19 00:59:57 CEST 2019

Last modified: Fri Apr 19 02:26:01 CEST 2019


After I [finally got 
around]( one of my first 
CTF I decided to recreate a variant of the vulnerable program in C.

The vulnerable program will do the following thing:

1. Ask for two numbers, namely *x* and *y*
2. Copy *y* at an address that depends on *x*

The fact that the address of *y*'s copy depends on *x* is a 

Our goal is to exploit this vulnerability so that the program runs 
win(), a function that was written in the source code but is normally 
never called by main().

First, [download]( and compile the vulnerable 

 <!--?prettify lang=bash?-->
cc ./test.c
# test.c:7:1: warning: control reaches end of non-void function [-Wreturn-type]
# }
# ^
# 1 warning generated.</code></pre>

Possibly without taking a look at it. Scroll down if you still want to, 
there is a simplified version at the bottom of the article so that you 
can get the idea.

So we now have a vunerable `a.out`.

Now onto disassembling it using objdump(1).

First of all, we need to take note of win()'s address.

<pre><code>0000000000201350 (win)</code></pre>

So 0x201350, which 2102096 in decimal.

Now skipping to the main procedure, we'll draw a small map of the stack.

<table border=1 style=float:right;margin:1em;margin-top:0>

Where SFP = Stack Frame Pointer, which we don't care about here, and RET 
= Return address, which is the address that will be moved in RIP when 
`ret` appears in main().

Moving further down in main(), we notice that scanf() is called.

 <!--?prettify lang=nasm?-->
<pre><code>call   201480 ; (scanf)
cmp    eax,0x2
je     2013bc ; (main+0x3c)
; [...]

Before returning, scanf() stores a number in EAX, in our case, the 
number must be `0x2` or else the program returns. It represents the 
number of items that were successfully processed from stdin.

Skipping whatever remains until the end of the function:

 <!--?prettify lang=nasm?-->
<pre><code>jmp    20138f ; (main+0xf)</code></pre>

We can deduce from this that main() is enclosed in an infinite loop.

Ignoring whatever happens after scanf() is successful; we're summing up 
our discoveries:

- main()'s stack,
- main() is enclosed in an infinite loop,
- the loop terminates when scanf() fail,

We can now look at the three remaining instructions.

 <!--?prettify lang=nasm?-->
<pre><code>mov    eax,DWORD PTR [rbp-0x10]
movsxd rcx,DWORD PTR [rbp-0x8]
mov    DWORD PTR [rbp+rcx*4-0x14],eax  ; &lt;= YOU ARE HERE</code></pre>

Where EAX = *y* and RCX = *x*, according to our stack table.

Simplifying this into a more math-friendly notation, we get
\[rbp + 4*x* - 0x14\] = *y*.

*If you want to experiment in GDB, you can type the following 
arithmetic formula: `$rbp + $rcx*4 - 0x14` to simulate.*

So *y*'s location in memory depends on *x*, which is user-supplied 
information. We have control. We can use *x* to reach any address 
(with respect to RBP) in writable memory and store *y*.

What if `$rbp+4*$rcx-0x14` is `$rbp+0x8`? If you remember it from our 
stack table, it's where the "Return address" is located. Address which 
is stored back in RIP on `ret` (which occurs in our code when scanf 
fails). It was placed there by the `call main` instruction. Were we to 
change this value that the program would then jump to whatever address 
is stored there when the function ends.

What we need:

- `$rbp+4*$rcx-0x14` to be equal to `$rbp+0x8`, by adjusting *x*.
- Something to store at `$rbp+0x8`, which we pass down via *y*.

What we have:

- a value for *y*, namely, the address of win(): 2102096,
- an equation that represent the value of *x*.

We're solving it right now.

![equation solved](assets/eq.jpeg)

So let's try this: *x* = 7 and *y* = 2102096.

<pre><code>$ ./a.out
7 2102096</code></pre>

Uh? What's wrong, nothing happens?

Scanf was successful once. It means a whole iteration of the main loop 
just occured. Scanf is called a second time and is still waiting for 
some input. Just type garbage: `*` for example and scanf() will fail: 
thus triggering win().

7 2102096*
You win!
Segmentation fault</code></pre>

Don't hesitate to comment for clarification or just for discussing CTFs! 
Bonus points: sharing resources is much appreciated.

Thanks for reading <3

**Code listing**

 <!--?prettify lang=c?-->

  int x, y, z, k;
  while (1) {
    if (scanf("%d %d", &x, &z) != 2)
      return 1;
    *(int *)(&k + x) = z;