I exploited a Format String Vulnerability in the clawdbot application to give me root priviledges, exploiting the "channeling project" discussed on the PDF given on the lessons website.. By providing a malicious format string as input, I was able to trick the printf function into writing an arbitrary value (the address of a backdoor function) to an arbitrary memory location (a global function pointer).
- Firstly it was pretty evident by looking at the clawdbot.c code that the main function uses sprintf to format a message buffer with user input and then calls "printf(message);". Because the first argument to printf is user-controlled and lacks a static format specifier (like "%s"), it is vulnerable.I also saw the function "void all_your_base_are_belong_to_us()", which is like the "thank_you" functions we saw that give us root access.
- To find the buffer's position on the stack, I used the technique from the paper. I ran: clawdbot 'AAAABBBB.%08x.%08x.%08x.%08x.%08x.%08x'.The output returned 41414141 (hex for AAAA) as the 4th value. This confirmed a stack offset of 4
- To verify control, I used the $ qualifier: clawdbot 'AAAA%4$x'. This successfully printed "Prompt received: AAAA41414141!", proving I could directly address my input.
To get control of the execution flow, I needed to identify what to write and where to write it.
- What: As i said in the .c file i found that "thank_you" function that spawns a root shell. I found its address using ```bash bot@9454b82fc4e6:/sbin$ nm clawdbot | grep "all_your" 08049c6d T all_your_base_are_belong_to_us
- WHere: I identified a global function pointer named process that is called immediately after the vulnerable printf - unfortunately didnt have time to think something else so i used process() :)
```bash
bot@9454b82fc4e6:/sbin$ nm clawdbot | grep "process"
0804e044 D process
- To achieve arbitrary code execution, I constructed a payload in Python that utilizes the Short Write technique from the paper to overwrite the process function pointer.
- To overwrite the 4-byte process pointer, I had to provide two target addresses at the beginning of my payload: the base address of the pointer and the base address plus two bytes. I used struct.pack() to "pack" these into raw bytes that the CPU can read from the stack. Because my target value for the high bits 0x0804 is smaller than the low bits 09c6d, I placed the address for the high bits first in the buffer to ensure the printf counter only increases.
- I used the %hn specifier, which writes the total number of characters printf has printed so far into a memory address. I treated printf as having an internal counter that I could manipulate using %u. The program's source code shows that a static prefix "Prompt received: " (17 characters) is printed before our input even begins. Combined with the 8 bytes of raw addresses I placed at the start of the buffer, the internal counter starts at 25 (17 + 8)
- To write the high bits 0804, I subtracted the current counter from the target: 2052 - 25 = 2027. By adding %2027u to the payload, the counter hits 2052, and %4$hn writes that value to the first address.
- To reach the low bits 0x9c6d (40045), I added more padding: 40045 - 2052 = 37993. Adding %37993u brings the counter to 40045, and %5$hn writes it to the second address.
The final exploit.py script automates this logic, using Direct Parameter Access ($) to jump straight to the addresses at stack offsets 4 and 5
