You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 0x08 [bug #76047] Bypassing disable_functions/README.md
+22-20Lines changed: 22 additions & 20 deletions
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,4 @@
1
-
# Bug #76047 - breaking``disabled_functions``
1
+
# Bug #76047 - Bypassing``disabled_functions``
2
2
3
3
>The exploit we'll talk about in this chapter is intended for **post-exploitation**. You'll use it cases where you have a File Upload vulnerability but the target PHP environment is hardened and you cannot execute dangerous functions such as ``system()``, ``passthru()``, ``exec()`` etc.
In the next lines of the exploit([187-188](exploit.php#L187)), the author uses the ``write()`` function to write the values ``2`` and ``6`` at specific offsets (``0x60`` and ``0x70``):
326
+
In the next lines of the exploit([187-188](exploit.php#L187-L188)), the author uses the ``write()`` function to write the values ``2`` and ``6`` at specific offsets (``0x60`` and ``0x70``):
327
327
```php
328
328
write($abc, 0x60, 2);
329
329
write($abc, 0x70, 6);
@@ -382,7 +382,7 @@ In other words: If we make ``$helper->a`` to point to the value in ``$addr``, wh
382
382
383
383
This is why in line 3 he subtracted 0x10, which is 16 in decimal. He wanted that ``zend_string.len`` will land exactly at the value that ``strlen()`` returns)
384
384
385
-
Example of a leak (Lines [195-196](exploit.php#L195) in the exploit):
385
+
Example of a leak (Lines [195-196](exploit.php#L195-L196) in the exploit):
386
386
387
387
The variable ``$closure_handlers`` contains the hex value ``0xef6dc0``:
All of the functions above (from ``get_binary_base()`` to ``get_system()`` at the end of the snippet) are not doing anything special. They are all just wrappers of ``leak()`` (which we already explained). Here's a short description of what they're doing:
476
476
477
-
1.``$base = get_binary_base($binary_leak)``: returns the binary base address by calling ``leak($binary_leak)`` over and over again in a loop until it hits the *ELF header* (if ``$leak == 0x10102464c457f`` is true then it returns the address)
477
+
1.``$base = get_binary_base($binary_leak)``: returns the binary base address by calling ``leak()`` over and over again in a loop until it hits the *ELF header* (if ``$leak == 0x10102464c457f`` is true then it returns the address)
478
478
2.``$elf = parse_elf($base)``: Returns an array with useful sizes and addresses of different segments in the memory ( ``return [$data_addr, $text_size, $data_size];`` )
479
479
3.``$basic_funcs = get_basic_funcs($base, $elf)``: Using the 2 previous values we leaked (above), we are retrieving the address of PHP's *basic functions*.
480
480
@@ -490,9 +490,9 @@ The ``functions`` property(in red) is our *basic funcs*. It contains internal PH
490
490
491
491
## Creating a Fake Closure Object
492
492
493
-
>**Reminder**: In the beginning of the exploit, we set ``$helper->b = function ($x) { }; ``. This is just an empty anonymous function (or, **[*closure*](https://www.php.net/manual/en/functions.anonymous.php) object in "Zend land"**) that accepts one argument and basically doing nothing. It's useless right now but soon we will turn it into ``system`` 🔫
493
+
>**Reminder**: In the beginning of the exploit, we set ``$helper->b = function ($x) { }; ``. This is just an empty anonymous function (or, **[*closure*](https://www.php.net/manual/en/functions.anonymous.php) object in "[Zend land](https://github.com/php/php-src/blob/PHP-7.1.0/Zend/zend_closures.c#L40-L46)**") that accepts one argument and basically doing nothing. It's useless right now but soon we will turn it into ``system`` 🔫
494
494
495
-
To call PHP's ``system()``, we are going to create a fake *closure object* in memory (Lines [214-218](./exploit.php#L214)):
495
+
To call PHP's ``system()``, we are going to create a fake *closure object* in memory (Lines [214-218](./exploit.php#L214-L218)):
1.``$closure_obj = str2ptr($abc, 0x20);``: we are retrieving the address of the ``zend_function`` which ``$helper->b`` points to. In the screenshot below you can see that we're printing the zval of ``$helper->b`` and getting the address of our anonymous ``zend_function``using ``zval.value.func``:
507
+
1.``$closure_obj = str2ptr($abc, 0x20);``: we are retrieving the address of the ``zend_closure`` which ``$helper->b`` points to. In the screenshot below you can see that we're printing the zval of ``$helper->b`` and getting the address of our anonymous function(or, closure object) using ``zval.value.obj``:
508
508
509
509

510
510
511
-
2.``for($i = 0; $i < 0x110; $i += 8) { ... } ``: in this part, we're using ``leak()`` and ``write()`` to copy the contents of our leaked ``$closure_obj`` pointer. We are copying it to be somewhere after the ``$helper`` object:
511
+
2.``for($i = 0; $i < 0x110; $i += 8) { ... } ``: in this loop, we're using ``leak()`` and ``write()`` to **copy** the contents of our leaked ``$closure_obj`` pointer. We are copying it to be somewhere after the ``$helper`` object in the memory:
512
512
513
513

514
514
515
-
In yellow: we can see ``$helper->c`` (0x1337) and we can also see the last property of the object, ``$helper->d`` (0x1338)
515
+
In yellow: we can see ``$helper->c`` (0x1337) and we can also see the last property of the PHP object, ``$helper->d`` (0x1338)
516
516
517
-
In red: This is where our new copy will land(at offset ``0xd0``), outside of the $helper obj
517
+
In red: This is where our new copy will land(at offset ``0xd0``), outside of the $helper PHP object.
518
518
519
-
After having a fresh copy of an anonymous function in memory, we'll start manipulating its metadata (Lines [221-225](./exploit.php#L221)):
519
+
After having a fresh copy of an anonymous function in memory, we'll start manipulating its metadata (Lines [221-225](./exploit.php#L221-L225)):
520
520
521
521
```php
522
522
write($abc, 0x20, $abc_addr + $fake_obj_offset);
523
523
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
1. In the 1st line: we rewrite the ``zval.value`` of ``$helper->b``, we're making it to point to our malicious copy of ``zend_function`` at offset ``0xd0``.
528
-
2.Line 2 and 3 are responsible for overwriting some of the metadata in the malicious ``zend_function``, you can see the overwrite below:
527
+
1. In the 1st line: we rewrite the ``zval.value.obj`` of ``$helper->b``, we're making it to point to our malicious copy of ``zend_closure`` object at offset ``0xd0``.
528
+
2.Lines 2 and 3 are responsible for overwriting some of the metadata in the malicious ``zend_closure`` object, you can see the overwrite below:
529
529
530
530

531
531
532
532
We can see from the diff that:
533
-
* We overwrote a useful function pointer and set it to be ``zif_system`` (Line 37 in the diff)
534
-
* We overwrote the ``zend_function.oparray.last`` value to be ``0x1`` instead of ``0x2`` (Line 29 in the diff). But why? 1 or 2, what does it matter? It took me a while to understand but eventually I realized why the exploit author did it: The Zend Engine calls a function named ``destroy_op_array()`` which causes a crash. But if we set this value to be ``0x1``, we can make this function skip the crash:
533
+
* We overwrote a useful function pointer and set it to be ``zif_system`` (Line 25 in the diff)
534
+
* We overwrote the ``zend_closure.func.type`` value to be ``0x1`` instead of ``0x2`` (Line 7 in the diff). But why? 1 or 2, what does it matter? It took me a while to understand but eventually I realized why the exploit author did it: The Zend Engine calls a function named ``destroy_op_array()`` which causes a crash. But if we set this value to be ``0x1``, we can make skip the crash:
By setting it to ``0x1``, the ``if()`` statement in line 445(in the snippet above) will not be true and ``destroy_op_array()`` will not be called because PHP thinks it's an internal function and not a user-defined function. #profit
538
+
By setting it to ``0x1``, the ``if()`` statement in line 445(in the snippet above) will not be true and ``destroy_op_array()`` will not be called. #profit
539
+
540
+
After looking at the macro from the snippet above(``#define ZEND_USER_FUNCTION 2``), I also realized that the exploit author set it to 0x1 and not 0x2 to make the Zend engine think that it's an internal function and not a user-defined function.
539
541
540
542
## Pwn 🔫
541
543
542
-
Now that we have our malicious ``zend_function`` set in``$helper->b``, all we need is to call it in a way that will trigger the ``try_catch_array`` function pointer:
544
+
Now that we have our malicious ``zend_closure``all-set inside``$helper->b``, all we need is to call it in a way that will trigger the ``zend_closure.func.internal_function.handler`` function pointer:
I personally had 99% success rate with this spraying so...it should not worry you. Try it yourself :D
581
+
I personally had 99% success rate with this spraying so...crashes should not worry you. Try it yourself :D
580
582
581
583
## More info:
582
-
* Black Hat USA Conference(2010) - Stefan Esser - Code Reuse/Return Oriented Programming in PHP Application Exploits (this one is pretty old but it's worth watching)
584
+
* Black Hat USA Conference(2010) - Stefan Esser - Code Reuse/Return Oriented Programming in PHP Application Exploits (**this one is pretty old but it's worth watching**)
583
585
- Part 1/5: https://www.youtube.com/watch?v=c0ZCe311YW8
584
586
- Part 2/5: https://www.youtube.com/watch?v=XP6KpKhDlg0
585
587
- Part 3/5: https://www.youtube.com/watch?v=rF9UK4dxtBs
0 commit comments