Skip to content
AndreQuimper edited this page May 8, 2024 · 7 revisions

PWN

If you want to get started on PWN it is heavily recommended that you first understand the basics of REV

Pwning is the art of exploiting an already running system. Generally there is a binary running on a server hosted by the CTF. They give the URL and port for us to open a connection. We can then interact with that server. Importantly we want to craft a malicious input that we can send which somehow redirects code execution on the server. Often times we want to "pop a shell" which means we cause the server program to turn itself into bash. This is outside the scope of the paragraph but that often entails sending an input that causes the program to run execve('/bin/sh').

We can then use that sell to do whatever we want in the server. Often this is just reading the filesystem which has a flag file. Do not worry about the challenge owner's server! If set up properly all instances all containerized.

However we don't want to find the exploit using the server. We usually want to download the challenge, exploit it locally, and then use the same solution on the remote.

Useful Commands
  • checksec <file> (can also do in pwndbg)
  • ropper -f <file>
  • ropper -f <file> --search <regex>

Challenges

Videos

https://youtu.be/oS2O75H57qU

Workflow

Identify each File Uploaded

There may be several types of files uploaded:

Source Code

This is a great way to find the vulnerability without having to open up Ghidra or disassemble inside of GDB. One of the first things you should do is analyze the usage of libc functions. For example:

  • Is gets called? This function is fundamentally unsafe. It lets the user read as many characters as they want into a specified buffer. This is classified as an overflow issue, meaning we can write to memory that we are not supposed to
  • Is fgets called with a size that is greater than the buffer it writes to? If so it is similar to using gets
  • Is printf called with direct user input? It turns out this is REALLY unsafe and gives us an arbitrary write primitive. What that means is we can write any value to any address we want to in the program.

Even if there is source code you should still open the binary in a static reversing tool such as Ghidra or IDA. It turns out compilers take some liberties when transferring the source code into assembly. You should also poke around and identify:

  • What assembly architecture is used? (x86, ARM, etc.)
  • What type of libraries are linked?
  • What strings are there?
  • Identify functions that do critical things and identify small helper functions

Dockerfile

Usually the Dockerfile is not actually part of the challenge. It just allows you to recreate the environment in which the server is running the binary. For example, often times knowing what version of libc is used is an integral part in solving the challenge. The Dockerfile allows you to build the challenge locally and then have access to that version. Often times I actually install all the tools inside the Docker container and then work there so everything works "first try" on the remote.

libc

Sometimes the challenge author just provide the libc directly. We can then place it in the same folder as the binary and if done right it will use that libc. This is a bit simpler than Docker.

Binary

The binary or executable. This is what is actually run on the server and what we are exploiting. As mentioned before this should be opened in static reversing tools first. You can also run it a few times and provide some input to get a sense of what the program is doing.

A must do is running checksec on this binary. There are four major mitigations that processes have on Linux:

  • PIE: Position Independent code means that this binary has ASLR turned on. In other words the segments will be at random addresses. For example the main function could be at 0x40000 in one launch and 0x60000 in another. Often times this means we need to first leak information before launching our actual attack.
  • NX: Non-executable bit. This means that code segments which don't have code cannot be executed. This is used to mitigate shellcode. For example placing assembly on the stack and then executing it. Often times this is a sign you need to use ROP chaining or a one gadget.
  • Stack Canary: Before the stack frame end there will be a unique value that is randomly generated at the launch of the program. It will be checked before the function executes its leave and ret instructions. So if you had a overflow but had to write over this the program would know and exit right away. You can either leak it it or get a more granular write primitive to skip over it.
  • RELRO: This means the PLT (Procedural Linkage Table) and GOT (Global Offset Table) have additional security measures. Specifically the GOT is read only. When you call a function to a linked library the code first goes to the GOT and looks up a value. One form of attacking is overwriting that table with a malicious address. This attempts to mitigate such behavior.

These settings HIGHLY affect how you craft your solution.

Learning Resources