Rating:
# Zeh (solved, 100p)
## Description
In the task we get a classic 64bit ELF linux binary where we have to give it two numbers for it to print out the flag. We are also given the C source code that created the binary.
## Static Analysis
Since we are given the source code we don't have to use a disassembler and can just open the source code in your favorite editor.
```C
#include <stdio.h>
#include <stdlib.h>
#include "fahne.h"
#define Hauptroutine main
#define nichts void
#define Ganzzahl int
#define schleife(n) for (Ganzzahl i = n; i--;)
#define bitrverschieb(n, m) (n) >> (m)
#define diskreteAddition(n, m) (n) ^ (m)
#define wenn if
#define ansonsten else
#define Zeichen char
#define Zeiger *
#define Referenz &
#define Ausgabe(s) puts(s)
#define FormatAusgabe printf
#define FormatEingabe scanf
#define Zufall rand()
#define istgleich =
#define gleichbedeutend ==
nichts Hauptroutine(nichts) {
Ganzzahl i istgleich Zufall;
Ganzzahl k istgleich 13;
Ganzzahl e;
Ganzzahl Zeiger p istgleich Referenz i;
FormatAusgabe("%d\n", i);
fflush(stdout);
FormatEingabe("%d %d", Referenz k, Referenz e);
schleife(7)
k istgleich bitrverschieb(Zeiger p, k % 3);
k istgleich diskreteAddition(k, e);
wenn(k gleichbedeutend 53225)
Ausgabe(Fahne);
ansonsten
Ausgabe("War wohl nichts!");
}
```
First things first, we have to translate it out of German.
```C
#include <stdio.h>
#include <stdlib.h>
#include "fahne.h"
void main(void) {
int i = rand();
int k = 13;
int e;
int *p = &i;
printf("%d\n", i);
fflush(stdout);
scanf("%d %d", &k, &e);
for(int i = 7; i--;)
k = (*p) >> (k%3);
k = (k)^(e);
if(k == 53225)
puts(Fahne);
else
puts("War wohl void!");
}
```
The code is a lot cleaner now and we can get some basic information:
- They are not setting a random seed so we know `rand()` will return 1804289383 everytime
- We need k to equal 53225 after all is said and done
- Our input is being bitshifted and XOR'ed
I thought this would be a good way to use angr since the loop will only ever go 7 times. This ensures that there wont be too many states for angr to go through.
## Disassembly
Lets open our binary in Ghidra to get the address we need to solve to.
```assembly
0010124c 48 8d 3d LEA RDI,[DAT_00102019]
c6 0d 00 00
00101253 e8 d8 fd CALL puts
00101258 eb 0c JMP LAB_00101266
```
We need to get to `CALL puts`(0x00101253) to consider this problem solved. We also know that the solution will have to be 4 bytes long because it's a C int.
## Solution
We have everything we need to code our solution:
```python
import angr
import claripy
import subprocess
import sys
program_name = "a.out"
find_address = 0x00001253
num_bytes_of_flag = 0x04
import logging
logging.getLogger('angr').setLevel(logging.INFO)
# num_keys = get_arg()
proj = angr.Project(program_name,
main_opts = {"base_addr": 0}, # PIE binary
)
# create an array of bitvectors so that the value of each can easily be
# constrained to the range of printable ASCII characters
key_bytes = [claripy.BVS("byte_%d" % i, 8) for i in range(num_bytes_of_flag*8)]
key_bytes_AST = claripy.Concat(*key_bytes)
# we want to generate valid keys, so a symbolic variable is passed to
# the state rather than a concrete value
state = proj.factory.full_init_state(args = ["./"+program_name], add_options=angr.options.unicorn)
# impose constraints on bitvectors the symbolic key is composed of
for byte in key_bytes:
state.solver.add(byte >= 0x21, byte <= 0x7e)
sm = proj.factory.simulation_manager(state)
# find path to message indicating key was correct
sm.explore(find = find_address)
if len(sm.found) > 0:
print("[ + ] %s" % (sm.found[0].posix.dumps(0)))
else:
print("[ x ] No solution found")
```
After about ~3 seconds Angr came up with two numbers that satisfy the program. Angr gave me "3578031137 1804307086" as a solution. Once given to the program it will print out the flag.
flag = CSR{RUECKWARTSINGENEUREN}