Rating:

## FTP (re, 300p, 214 solves)

> We found an ftp service, I'm sure there's some way to log on to it.
>
> nc 54.172.10.117 12012
> [ftp_0319deb1c1c033af28613c57da686aa7](ftp)

### PL Version
`for ENG version scroll down`

Pobieramy zalinkowany plik i ładujemy do IDY. Jest to faktycznie, zgodnie z opisem, serwer FTP.

W stringach znajdujących się w binarce znajdujemy napis zawierający wszystkie komendy wspierane przez serwer (w helpie):

> USER PASS PASV PORT
> NOOP REIN LIST SYST SIZE
> RETR STOR PWD CWD

Przeglądamy chwilę funkcje znajdujące się w binarce, i najciekawsza wydaje sie ta odpowiadająca "nieudokumentowanej" funkcji RDF:

mov [rbp+ptr], rax
mov esi, offset aR ; "r"
mov edi, offset filename ; "re_solution.txt"
call _fopen
; (...)
mov rdx, [rbp+stream]
mov rax, [rbp+ptr]
mov rcx, rdx ; stream
mov edx, 1 ; n
mov esi, 28h ; size
mov rdi, rax ; ptr
call _fread
mov rax, [rbp+var_18]
mov eax, [rax]
mov rdx, [rbp+ptr]
mov rsi, rdx
mov edi, eax
call send_string_to_client

Niestety, wywołanie tej funkcji wymaga autentykacji do systemu. Patrzymy więc na funkcje odpowiadającą za zalogowanie.

Wygląda ona mniej więcej tak (po ręcznym przepisaniu do C)

unsigned hash(char *txt)
{
int v = 5381;
for (int i = 0; txt[i]; ++i )
v = 33 * v + txt[i];
return (unsigned)v;
}

bool login_ok(char *username, char *password) {
return strcmp(username, "blankwall") == 0 && hash(password) == 3548828169;
}

(A przynajmniej to ważne fragmenty z tej funkcji, samo wczytywanie i wysyłanie tekstu do klienta pominęliśmy).

Funkcja hashująca jest jak widać bardzo prosta, więc można było spróbować ją złamać. I nie byłoby to bardzo trudne, ale poszliśmy prostszą drogą - zauważyliśmy że jest "monotoniczna" (czyli każdy kolejny znak w haśle ma coraz mniejszy wpływ na wynik hasha, czyli możemy zgadywać hasło znak po znaku). Napisaliśmy do tego narzędzie:

int main(int argc, char *argv[]) {
char c[1000];
puts("3548828169");
unsigned rzecz = rzeczy(argv[1]);
printf("%u\n", rzecz);
if (rzecz > 3548828169) {
puts("2much");
} else if (rzecz < 3548828169) {
puts("2low");
} else {
puts("just enough");
}
}

Przykładowa interakcja z programem (z komentarzami):

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Taaaaa
3548828169
3538058430
2low (czyli `Ta` to za niski prefiks)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Tlaaaa
3548828169
3551103561
2much (czyli `Tl` to za wysoki prefiks)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Tkaaaa
3548828169
3549917640
2much (czyli `Tk` to za wysoki prefiks)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Tjaaaa
3548828169
3548731719
2low (czyli `Tj` to za niski prefiks)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe TkCaaa
3548828169
3548839530
2much (czyli `Tk` jednak było ok, teraz próbujemy zmniejszyć trzeci znak)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe TkBaaa
3548828169
3548803593
2low (czyli `TkC` jest dobrym strzałem, bo `TkCa` jest za wysokie, `TkBa` już jest za niskie)

(itd, itd)

W ten sposób trafiamy na hasło - TkCWRy. Przy wpisywaniu go do nc trzeba pamiętać żeby zakończyć wpisywanie za pomocą C-d zamiast entera, bo inaczej hash liczy się ze znakiem nowej linii i wychodzi błędny.

Więc mamy hasło i usera, wystarczy wykonać komendę pobierającą flagę:

$ nc -vv 54.172.10.117 12012
Connection to 54.172.10.117 12012 port [tcp/*] succeeded!
Welcome to FTP server
USER blankwall
Please send password for user blankwall
PASS TkCWRylogged in
RDF
flag{n0_c0ok1e_ju$t_a_f1ag_f0r_you}

Gotowe.

### ENG Version

We download linked file and we load it with IDA. It is, in fact, a FTP server, just as described.

In the strings in the binary we find all the commands supported by server (in help):

> USER PASS PASV PORT
> NOOP REIN LIST SYST SIZE
> RETR STOR PWD CWD

We look around the functions in the binary and the most interesting one seems to be the "undocumented" RDF function:

mov [rbp+ptr], rax
mov esi, offset aR ; "r"
mov edi, offset filename ; "re_solution.txt"
call _fopen
; (...)
mov rdx, [rbp+stream]
mov rax, [rbp+ptr]
mov rcx, rdx ; stream
mov edx, 1 ; n
mov esi, 28h ; size
mov rdi, rax ; ptr
call _fread
mov rax, [rbp+var_18]
mov eax, [rax]
mov rdx, [rbp+ptr]
mov rsi, rdx
mov edi, eax
call send_string_to_client

Unfortunately, calling this function requires authentication. Therefore we check the login function.
It looks like this (after re-writing it to C):

unsigned hash(char *txt)
{
int v = 5381;
for (int i = 0; txt[i]; ++i )
v = 33 * v + txt[i];
return (unsigned)v;
}

bool login_ok(char *username, char *password) {
return strcmp(username, "blankwall") == 0 && hash(password) == 3548828169;
}

(Those are at lest the important parts, without the I/O operations).

As can be seen the hash function is very simple so we could try to break it. It would not be very difficult, however we decided to take an even easier path - we noticed that the hash function is "monotonic" (every next character in the password has smaller influence on the ouput hash, so we can guess the password character by character). We prepared a tool for this:

int main(int argc, char *argv[]) {
char c[1000];
puts("3548828169");
unsigned rzecz = rzeczy(argv[1]);
printf("%u\n", rzecz);
if (rzecz > 3548828169) {
puts("2much");
} else if (rzecz < 3548828169) {
puts("2low");
} else {
puts("just enough");
}
}

An example of interaction with the script (with comments):

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Taaaaa
3548828169
3538058430
2low (so `Ta` is too low for a prefix)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Tlaaaa
3548828169
3551103561
2much (so `Tl` is too high for a prefix)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Tkaaaa
3548828169
3549917640
2much (so `Tk` is too high for a prefix)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe Tjaaaa
3548828169
3548731719
2low (so `Tj` is too low for a prefix)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe TkCaaa
3548828169
3548839530
2much (so `Tk` was in fact OK, so now we try to lower the third character)

msm@andromeda /cygdrive/c/Users/msm/Code/RE/CTF/2015-09-16 csaw/pwn_300_ftp
$ ./a.exe TkBaaa
3548828169
3548803593
2low (so `TkC` is a good guess, since `TkCa` is too high and `TkBa` is too low)

(etc, etc)

This was we get the password - TkCWRy. It's worth noting to provide the input via nc and end it with C-d instead of Enter, otherwise the hash will include newline character and we get a wrong password.

So we have a password and username, we just need to issue a command to get the flag:

$ nc -vv 54.172.10.117 12012
Connection to 54.172.10.117 12012 port [tcp/*] succeeded!
Welcome to FTP server
USER blankwall
Please send password for user blankwall
PASS TkCWRylogged in
RDF
flag{n0_c0ok1e_ju$t_a_f1ag_f0r_you}

Done.

Original writeup (https://github.com/p4-team/ctf/tree/master/2015-09-16-csaw/re_300_ftp#eng-version).