We're working with a x64 binary and its is stripped
The protections enabled are NX and PIE and here's what they do:
1. NX(No-Execute) prevents shellcode injection to the stack and execution of the shellcode
2. PIE randomize memory addresses
I'll run it to know what it does
Using ghidra i'll decompile the binary
From the entry function we can get the main function
The main function address is FUN_001010a0
undefined [16] FUN_001010a0(void)
{
int iVar1;
ulong in_RCX;
char cVar2;
FUN_0010121e();
puts("========= HTB PwnShop ===========");
while( true ) {
while( true ) {
puts("What do you wanna do?");
printf("1> Buy\n2> Sell\n3> Exit\n> ");
iVar1 = getchar();
getchar();
cVar2 = (char)iVar1;
if (cVar2 != '2') break;
FUN_0010126a();
}
if (cVar2 == '3') break;
if (cVar2 == '1') {
FUN_0010132a();
}
else {
puts("Please try again.");
}
}
return ZEXT816(in_RCX) << 0x40;
}
We can see what it does:
1. Prints out the banner thingy
2. Gets our option and the available option is just 1,2 and 3
3. If 2 is chosen it calls the function for option2 same applies to 1 and 3
4. But if a given value is is not among the option it prints out please try again
Here's the decompiled function if option 1 is chosen
void FUN_0010132a(void)
{
undefined auStack72 [72];
puts("Sorry, we aren\'t selling right now.");
printf("But you can place a request. \nEnter details: ");
read(0,auStack72,0x50);
return;
}
From reading the code we can see that it receives our input and reads in 80 bytes of data in a 70 bytes buffer so there's a buffer overflow here
Here's the decompiled function if option 2 is chosen
void FUN_0010126a(void)
{
int iVar1;
long lVar2;
undefined4 *puVar3;
byte bVar4;
undefined4 auStack72 [8];
undefined8 local_28;
undefined4 *local_20;
bVar4 = 0;
local_20 = &DAT_001040c0;
printf("What do you wish to sell? ");
local_28 = 0;
puVar3 = auStack72;
for (lVar2 = 8; lVar2 != 0; lVar2 = lVar2 + -1) {
*puVar3 = 0;
puVar3 = puVar3 + (ulong)bVar4 * -2 + 1;
}
read(0,auStack72,0x1f);
printf("How much do you want for it? ");
read(0,&local_28,8);
iVar1 = strcmp((char *)&local_28,"13.37\n");
if (iVar1 == 0) {
puts("Sounds good. Leave details here so I can ask my guy to take a look.");
puVar3 = local_20;
for (lVar2 = 0x10; lVar2 != 0; lVar2 = lVar2 + -1) {
*puVar3 = 0;
puVar3 = puVar3 + (ulong)bVar4 * -2 + 1;
}
read(0,local_20,0x40);
}
else {
printf("What? %s? The best I can do is 13.37$\n",&local_28);
}
return;
}
Here's what it does:
1. It first receives our input which reads 31 bytes of our input and stores it in a 8bytes buffer
2. Our input is received again but this time it reads 8 byte of our input to and stores it in &local_28
So there's another buffer overflow in this part of the code
After i tried running the binary and just trying to overflow it i noticed if you feed in 7 or 8 bytes to the second input of option 2 it leaks some value
Here's whats happening
undefined8 local_28;
undefined4 *local_20;
bVar4 = 0;
local_20 = &DAT_001040c0
printf("How much do you want for it? ");
read(0,&local_28,8);
iVar1 = strcmp((char *)&local_28,"13.37\n");
Our input is overwriting the new line character and leaking address
But the address it leaks is &DAT_001040c0
which is the pointer to local_20
Also note that DAT_001040c0
is in the .bss
section of the binary
We know that pie is enabled so first we need to get the pie base address before we can continue exploitation
So the idea is that the value that is being leaked is going to be &DAT_001040c0
with that we can calculate the piebase address
Using ghidra i get the offset of the &DAT_001040c0
section which is 0x40c0
Here's the script to leak the pie base address Leak
Now lets do some exploitation 🤓
Lets get the offset
Looking at the vulnerable function of the code
void FUN_0010132a(void)
{
undefined auStack72 [72];
puts("Sorry, we aren\'t selling right now.");
printf("But you can place a request. \nEnter details: ");
read(0,auStack72,80);
return;
}
We're allowed to write in 80 bytes in a 72 bytes buffer. The amount of space we can just overwrite is just 8
bytes
And 8 bytes isn't enough for us to perform some roping so we need to increase the amount of bytes we can write to the stack
A way of doing this is by doing stack pivoting which basically will free more space for us on the stack
We need a gadget to subtract data off the stack. Using ropper i get a gadget
This 0x0000000000001219: sub rsp, 0x28; ret;
looks okay cause it subtracts 0x28 off the rsp
I'll also get a pop_rdi gadget using ropper
Here's the way to get the offset
The offset is 72
I'll just perform a ret2libc attack. Since we know that the got of puts is already going to be populated since its already called in the main function
Also since its a stripped binary i can't just call elf.symbols['main']
so basically after i leak the got of puts i'll need to return back to the vulnerable function to perform another rop chain
This is the way the payload is going to be
Overflow + Leak puts got + Return to function thats vuln to buffer overflow (FUN_0010132a)
This is the script i used for it Script
I decided to directly run it on the remote server cause i'm having issue running the final exploit locally
And since the remote server will likely use a different libc from ours lets leak
Running it remotely leaks the address
So now we can use LibcDB to get the likely libc the remote file uses
The libc is going to between the 3rd and 4th since the binary is x64
Looking at it shows the offset to various syscalls
I downloaded the libc file
┌──(mark㉿haxor)-[~/…/BofLearn/Challs/HTB/pwnshop]
└─$ ls -l libc6_2.23-0ubuntu11.2_amd64.so pwnshop
-rw-r--r-- 1 mark mark 1868984 Jan 26 2021 libc6_2.23-0ubuntu11.2_amd64.so
-rwxr-xr-x 1 mark mark 14384 Nov 18 2020 pwnshop
Now here's my exploit Exploit
And i had to automate it using rop object cause i had issue with data receive 😞
But anyways here's what the exploit does
1. It leaks the pie base address
2. Leaks puts got
2. Calculates the libc base address
3. Run system('/bin/sh')
And we're done 👻