In this tutorial, we'll learn about basic socket programming in Python, and techniques for remote exploitation.
nc
The netcat command -- or nc for short -- is similar to the cat command, but for networking.
netcat
cat
Here's a simple demo. Open two console windows side-by-side, and run a command in each, as shown below:
$ nc -l 1234
$ nc localhost 1234
The nc [address] [port] command connects to a server which is running at the given address and port. ("localhost" is an alias of 127.0.0.1, which is a reserved IP address that refers to your own computer.) nc -l [port] listens for connections to the given port, thus creating a very simple server.
nc [address] [port]
address
port
127.0.0.1
nc -l [port]
Now type "hello" in console window 2 and hit <enter>:
<enter>
hello
Did you get the "hello" message in console 1? What if you type "world" as a reply in console 1?
You've just created a nice chat program! You can talk to your fellow students on our server this way :)
If it doesn't work, someone else may already be using port 1234 on the lab server. You can try a different port number, or try it on your local machine instead. This advice applies to the rest of the tutorial, too -- it's a good idea to change port numbers from the defaults when trying things on the lab server.
If it doesn't work, someone else may already be using port 1234 on the lab server. You can try a different port number, or try it on your local machine instead.
This advice applies to the rest of the tutorial, too -- it's a good idea to change port numbers from the defaults when trying things on the lab server.
Today's goal is to beat the computer in a game of rock-paper-scissors!
First, execute target and let it listen to some arbitrary port (e.g., 1234).
target
$ ssh lab07@<ctf-server-address> $ mkdir /tmp/<your-secret-dir> $ cd /tmp/<your-secret-dir> $ cp -rf ~/tut07-socket ./ $ ./target 1234
In another console, use nc to connect to the target server that you just started:
$ ssh lab07@<ctf-server-address> $ nc localhost 1234 Let's play rock, paper, scissors! Your name>
FYI, on the server, the target service is already running on port 10700, and that's the one you'll need to beat to get the flag. You can also remotely connect to the service from outside the server:
$ nc <ctf-server-address> 10700
Do you want to explore the program a bit?
$ nc localhost 1234 Let's play rock, paper, scissors! Your name> cs6265 Your turn> rock You lose! Game over
You have to win 5 times in a row to win the whole game... so the odds aren't TOO bad for brute-forcing.
Let's use pwntools for socket operations. The following code snippet opens a socket on port 1234, reads 10 bytes from it, and writes them back to it:
from pwn import * s = remote("localhost", 1234) s.send(s.recv(10)) s.close()
We've provided some template code (template.py) to help you write a socket client program in Python.
$ ./target 9736
$ ./template.py
[Task] Your first task is to understand the template and write code that brute-forces the target server! Just by playing the same move five (or more) times, you have a pretty high chance of winning (1/2^5 = 1/32)!
[Task] Your first task is to understand the template and write code that brute-forces the target server!
Just by playing the same move five (or more) times, you have a pretty high chance of winning (1/2^5 = 1/32)!
Brute-forcing is dumb -- let's try a smarter approach.
Here's the most interesting part of target.c:
target.c
void start(int fd) { write(fd, "Let's play rock, paper, scissors!\nYour name> ", 44); char name[0x200]; if (read_line(fd, name, sizeof(name) - 1) <= 0) { return; } srand(*(unsigned int*)name + time(NULL)); int iter; for (iter = 0; iter < 5; iter ++) { write(fd, "Your turn> ", 11); char input[10]; if (read_line(fd, input, sizeof(input) - 1) <= 0) { return; } int yours = convert_to_int(input); if (yours == -1) { write(fd, "Not recognized! You lost!\n", 26); return; } int r = rand(); int result = yours - r % 3; if (result == 0) { write(fd, "Tie, try again!\n", 16); iter --; continue; } if (result == 1 || result == -2) { write(fd, "You win! try again!\n", 20); } else { write(fd, "You lose! Game over\n", 20); return; } } write(fd, "YOU WIN!\n", 9); dump_flag(fd); }
Did you notice the use of srand() and name as a seed for the game?
srand()
name
srand(*(unsigned int*)name + time(NULL));
Since the name variable is what you've provided, and the time is predictable, you can abuse this information to win the match every single time! (If only it was always that easy to win jackpots...)
In order to do that, you need to be able to call C functions such as srand() and rand() from Python, so let's discuss how to do that. (This is similar to the "weak-random" challenge from lab04, so this might be familiar if you solved that -- or it might not, since there are a variety of ways to do it!)
rand()
ref. https://docs.python.org/3/library/ctypes.html
from ctypes import * # How to invoke a C function in Python: libc = cdll.LoadLibrary('libc.so.6') libc.printf(b'hello world!\n')
This is how you can directly invoke the printf() function from Python. How would you invoke srand()/rand()?
printf()
There are several ways to cast a C string to an unsigned int in Python. When using pwntools, the best way is to use the u32() function, which is the inverse of the p32() function you're probably familiar with by now:
u32()
p32()
from pwn import * print(u32(b'test')) # prints 1953719668
Why is it 1953719668? The ASCII values for "t", "e", "s" and "t" are 0x74, 0x65, 0x73, 0x74. Because x86 is a little-endian architecture, four-byte integers are written and read in reversed byte order, i.e., 74 73 65 74. And 0x74736574 is 1953719668!
0x74
0x65
0x73
74
73
65
0x74736574
If you understand (1) and (2) above, you're ready to reliably beat the computer. Write a script that guesses the rand() output of the target and sends the winning move every time.
Once you get it working, don't forget that you can only get the "real" flag from port 10700 on our lab server:
[Task] Guess the output of the target's rand() and send the winning move five times in a row to defeat the computer. Then submit the flag it prints.
Good luck!