Category: Reversing
Points: 200
This service is a some kind of weird library system, which has the following functions:
- View the book title. You’ll have to be the “head librarian” ( = admin) first.
- View the book content (If you know the exact book title).
- Convert a binary string into a hex string (which is useless in this challenge).
- Let user to prove that he is the admin (will explain later).
According to the description, our goal is to read the book’s content. To achieve this, we’ll have to know the book title first, which can only be viewed by the “head librarian” ( = admin). So the main challenge is to prove that we’re the head librarian.
To prove that we’re the admin, we’ll have to guess the correct value of a 4 byte data ( which is read from /dev/urandom
) for 10 times continuously. This seems impossible, since we have no output information of the random value. I was kind of stuck at that point for quite a while, until I found something weird in the function which check the user input:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
signed __int64 __fastcall check_invalid_char(char a1)
{
signed __int64 result; // rax@2
if ( a1 > '/' )
{
if ( a1 > '9' )
{
if ( a1 > '@' )
{
if ( a1 > 'F' )
result = 0xFFFFFFFFLL;
else
result = (unsigned int)(a1 - 55);
}
else
{
__asm { syscall } // <-- WTF?
result = 0xFFFFFFFFLL;
}
}
else
{
result = (unsigned int)(a1 - '0');
}
}
else
{
result = 0xFFFFFFFFLL;
}
return result;
}
The input checking function will check every character to make sure that it is a valid hex char. Once it found out that the character was invalid, it will return -1
. What really caught my eye was the pseudo code __asm { syscall }
. Why did it use the syscall
instruction? Let’s check the assembly:
1
2
3
4
5
6
.text:0000000000400B21 mov rax, 0Eh
.text:0000000000400B28 mov rdi, 0
.text:0000000000400B2F mov rsi, 0
.text:0000000000400B36 mov rdx, 603397h
.text:0000000000400B3D mov r10, 8
.text:0000000000400B44 syscall
So…it just call the system call sigprocmask(0, NULL, 0x603397)
. After checking the man page, I still don’t know what sigprocmask
does, but I found out that it clears the byte at 0x603397
. This discovery is important, because in order to be the head librarian, we’ll have to let the data at 0x603394
be set to 0x00278F03
. If we can find a way that set the value at 0x603394
to 0xXX278F03
and trigger the sigprocmask
syscall, then 0xXX278F03
will become 0x00278F03
, which will give us the admin privileges!
After doing some analysis, I finally figured out how to beat the challenge:
- First, send the input
420B65F7
- The program will then ask us to input the “library card”. We send
99278F03
instead of00278F03
, because if we send00278F03
, program will ask us to guess the random number, and it’ll set the value at0x603394
to0
after we failed to guess the correct value. - By sending
99278F03
, value at0x603394
will become99278F03
. It will failed the check though, but since the program did not ask us to guess the random number, it won’t clear the data at0x603394
, and so the value at0x603394
will remain unchange. - Now we send an invalid input in order to trigger the
sigprocmask
syscall. The program will clear a byte at0x603397
, changing99278F03
into00278F03
. - Now we can claim that we’re the head librarian and check the book title + read the book content.
Here the script that I wrote to pass the challenge:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python
from pwn import *
HOST = "school.fluxfingers.net"
PORT = 1527
def my_recvuntil(s, delim):
res = ""
while delim not in res:
c = s.recv(1)
res += c
sys.stdout.write(c)
sys.stdout.flush()
return res
def myexec(cmd):
return subprocess.check_output(cmd, shell=True)
if __name__ == "__main__":
r = remote(HOST, PORT)
r.sendlineafter("words.\n", "420B65F7")
r.sendlineafter("card.\n", "99278F03")
r.sendlineafter("alright!\n", ":\n123456") # trigger the sigprocmask syscall
r.sendlineafter("right?\n", "952A7224")
r.sendlineafter("16F7F4D391F030CF\n------------\n", "F1140B88")
r.recvuntil("book.\n")
r.sendline("16F7F4D3")
r.sendline("91F030CF")
r.interactive()
1
2
3
4
5
6
7
8
9
bruce30262@ubuntu:~/Desktop/hack.lu_2015/secret$ ./exp_secret.py
[+] Opening connection to school.fluxfingers.net on port 1527: Done
[*] Switching to interactive mode
oh, yes, we have that! here you go...
flag{our_secret_is_that_we_really_just_have_this_one_book}
====================
$
Flag: flag{our_secret_is_that_we_really_just_have_this_one_book}
Comments powered by Disqus.