Logic flaws
A logic flaw occurs when a programmer thinks a user will do always as they expect, and fail to handle scenario in which they don't.
- The user visit the page "payment" before the page "checkout"
- The user manually send a form and do not use the HTML form
A more concrete example is that if the developer used an HTML SELECT
tag with a list of countries, the developer may not check the submitted value as they expect it to be within their list of countries.
Mass Assignment
In an effort to simplify database interactions, it's common to use ORM (Object-relational mapping) which are objects linked to the database (erasing the SQL code behind).
They are often vulnerable to mass assigment in which we can update fields that we were not "supposed" to.
For instance, if we can change our username and add the field status
, we may be able to change our status using this route.
-{"username":"toto"}
+{"username":"toto","status":"admin"}
π» See also: Ruby/Rails.
PHP $_REQUEST logic flaw
The PHP variable $_REQUEST
is sometimes used to access $_GET
and $_POST
. It's a merge of both, in case of conflict, $_POST
values win.
It may be used for convenience, for instance, when chaining a $_POST
form to a $_GET
form. The problem is that if someone manually add key/values in $_POST
, they can erase values in $_GET
.
There is a logic flaw if the developer do not check previously checked values, such as the previous form $_GET
values, and assume that after checking them once, they are "safe" now.
Illustration πΊ
The fist form is using GET. The developer check the parameter n
β
.
$ curl 'URL/step1?n=5'
In the second form, the user submit a value in a POST form. The developer only check the new value square
β.
$ curl 'URL/step2?n=5' -d 'square=36'
But, if the user used:
$ curl 'URL/step2?n=5' -d 'square=36&n=6'
Then, in $_REQUEST['n']
there would be 6
, an uncheck value.
PHP Loose Comparison And Type Juggling
PHP supports loose (==, !=, <>) and strict (===, !===) comparison. When using loose comparison, PHP cast variables to the same type using Type Juggling before comparing their values. In PHP 5.6 (docker) and older, some results are quite surprising:
"abc" == 0
isTrue
(<ignored_invalid> == 0)"1a2b3" == 1
isTrue
(1<ignored_invalid> == 1)"12abc3" == 12
isTrue
(12<ignored_invalid> == 12)- ...
Additionally, "abc" == True
is True
for all versions.
πͺ¦ PHP Type Juggling is difficult to exploit as usually GET/POST data are strings unless they are unserialized using JSON.
Loose Comparison β Exploiting STRCMP
The strcmp
function may be also exploited. In PHP 5.6, it returns an integer which indicates the number of different characters.
strcmp("abc", "abd") == 0
isFalse
strcmp("abc", "abc") == 0
isTrue
strcmp([], "abd") == 0
isTrue
(Returns NULL, NULL==0 is True)
π Refer to HTTP Parameter Pollution
PHP Magic Hashes
Even in PHP 8 and newer, the following equalities are TRUE:
"0e299969168079221277306999992834" == "0"
"0e66507019969427134894567494305185566735" == "0e1337"
"0e66507019969427134894567494305185566735" == "0e4242424242"
There are known plaintext that result in a hash starting with 0e
. Refer to spaze/hashes (0.7k β) repository.
hash("md2", 'Oq9wqi64') == "0"
isTrue
hash("sha1", 'aaroZmOk') == "0e1337"
isTrue
- ...
// Get A Magic Hash For Hash Type 'fnv164'
while (1) {
$p = bin2hex(random_bytes(8));
$h = hash('fnv164', $p);
if ($h == "0") {
echo "Found hash($p)=$h";
break;
}
}
β οΈ Fun note: bcrypt
truncates input longer than 72 characters.
HTTP Parameter Pollution
Parameter parsing and prioritization vary based on the underlying web technology in use. This may be exploited in various ways.
Common examples are in PHP/Javascript that are dynamically typed and inferring the type of the parameter based on the use of []
.
// PHP
"https://example.com/?username=abc" # String
"https://example.com/?username[]=abc" # Array
// JavaScript
"https://example.com/?username=abc" # String
"https://example.com/?username[0]=abc" # Array
This can be very helpful to exploit type juggling such as explained here for PHP while in JS you can bypass size checks with that.
Python Werkzeug Debug Pin (Flask)
Flask is built on top of the Werkzeug webserver. It has a debugger that can be exploited to gain RCE. Try to access /console
to see if it's enabled. You will be prompted for a pin.
According to the documentation: "If an incorrect PIN is entered too many times the server needs to be restarted.".
We can compute the PIN on our machine assuming we manage to get the necessary information from the target.
private_bits = [
# /proc/net/arp (ex: eth0) => /sys/class/net/eth0/address
str(int('00:00:00:00:00:00'.replace(':', ''), 16)),
'machine_identifier'
]
To get the machine identifier, concatenate the content of /etc/machine-id
or /proc/sys/kernel/random/boot_id
with the last value of the first line (slash-separated, can be empty) of /proc/self/cgroup
.
probably_public_bits = [
# User running the app (/etc/passwd + /proc/self/status)
'username_here',
# Usually left unchanged
'flask.app', 'Flask',
# 'werkzeug.debug', 'DebuggedApplication',
# 'flask.app', 'wsgi_app'
# We are looking for "flask/app.py"
# /usr/local/lib/pythonX/site-packages/
# /home/<username>/.local/lib/pythonX/site-packages/
'/usr/local/lib/python3.10/site-packages/flask/app.py'
]
Refer to hacktricks for the script, while you need to update the two variables for the PIN to be the correct one.
π See also: werkzeug_debug_rce
(secret exposed in HTML?)
Random Notes
Execution After Redirect (EAR)
It occurs when a user is redirected, but if they don't follow the redirection, then they can still access the page.
In PHP, it would occur if the developper forgot to call exit
as any code after the redirecting is still executed.
You can use ncat
to read a page without being redirected.
$ nc domain 80
GET /URI HTTP/1.1
Host: domain
PHP Register Globals
When using register_globals
, global variables such as $_GET['param']
are automatically mapped to a global variable such as $param
.
It applies to $_GET
, $_POST
, $_COOKIE
, $_REQUEST
, etc.
This feature is deprecated since PHP 5.3.0, while the extract(ARRAY)
function is still present and maybe still be used.
Exploitation
- Set
$token
: useURL/?token=xxx
. - Set
$_SESSION['key']
: useURL/?_SESSION[key]=xxx
.
Client-Side Logic
While uncommon, if the logic is client-side and only the result is sent to the server, we can do whatever we want.
PHP Remote Xdebug
Xdebug allows developers to remotely debug a PHP web application. This gist provide multiple interesting details. If remote_connect_back
, anyone can run PHP commands on the server.
- Create a file in your IDE (ex: index.php, any name)
- Add code (e.g.
<?php\nphpinfo();
) and a breakpoint - Listen for PHP debug connections (ex: PHPStorm>Bottom-right icon)
- Load the page supporting Xdebug
- Go back to your IDE and enjoy
For PHPStorm, refer to the debug tool window overview.
π» To-do π»
Stuff that I found, but never read/used yet.