Skip to content

Commit 630c751

Browse files
author
bigshaq
committed
init
1 parent f6747ca commit 630c751

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2264
-1
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
# Digging into ``ZipArchive::extractTo()``
3+
In this post, I will show something that I found in PHP’s source code and affects all versions of PHP.
4+
During my PHP Internals research, I reviewed the code of ``ZipArchive::extractTo()``, which is responsible for extracting a zip file(opened using ``ZipArchive::open()``) and found an interesting thing.
5+
6+
## Description
7+
Let’s take this example PHP script:
8+
```php
9+
<?php
10+
$zip = new ZipArchive;
11+
if ($zip->open('test.zip') === TRUE) {
12+
$zip->extractTo('/tmp/phpfuzz/outdir/');
13+
$zip->close();
14+
echo 'ok';
15+
} else {
16+
echo 'failed';
17+
}
18+
?>
19+
```
20+
This script will extract the contents of ``test.zip`` into a directory called “outdir”.
21+
22+
The issue here is that, by default, PHP will create ``outdir/*`` to be with hard-coded 777 permissions:
23+
24+
https://github.com/php/php-src/blob/5fa17fbf94fa95705922313ac0d19e38ddabbad9/ext/zip/php_zip.c#L2781
25+
26+
![screenshot1](./images/1.png)
27+
28+
This is the directory tree after un-zipping the file:
29+
30+
![screenshot1](./images/2.png)
31+
32+
Everything is readable to the public usergroup. And it's recursive (sub-directories are affected as well).
33+
34+
>**Note**: In other systems it can be a "full 777 permission"(like ``rwxrwxrwx``) but on my system it's not due to my *uname* setting (I kept the default setting so you can see a real-life scenario).
35+
36+
## Impact / Threat
37+
38+
This affects mostly applications on shared hosting environments.
39+
40+
On a shared hosting environment, all websites/applications are stored on the same server and each website has its own Linux user & group (this is not correct for all shared hosting providers, but this is the common design that most of the shared-hosting providers prefer to stick with).
41+
42+
![screenshot1](./images/3.png)
43+
44+
Making the extracted files & directories readable by the “public” usergroup (in the screenshot above, called “Other”) allows other applications hosted on the same Linux server to read the extracted zip content. This can be easily done using symlinking technique (more about symlinks: https://www.cybrary.it/blog/2019/07/symlink-attacks/)
45+
46+
**Example scenario**:
47+
1. A PHP application has a feature that's triggering a call to the ``extractTo()`` method. This can happen during plugin installation or any other reason for unpacking zip files.
48+
49+
The files will be extracted to: ``/home/victim/public_html/extracted_here/``
50+
51+
2. All of the extracted files (which might contain sensitive info such as configurations) are readable to all users on the server. All of the directories are readable too and can be listed by a malicious user on the server.
52+
53+
3. In order to disclose those extracted files, the attacker buys a basic hosting plan on the same shared-hosting of the target application (or alternatively, take over another vulnerable website that is stored there)
54+
55+
The attacker’s home directory: ``/home/appsec/public_html/``
56+
57+
4. When the attacker will try to access the victim’s files, he will try the following:
58+
```
59+
appsec@server:$ ls /home/victim/public_html/
60+
```
61+
This attempt will (most likely) not work because the server is configured properly and the only bypass will be at the OS level.
62+
63+
**However**, if there's a directory/file which is created by ``ZipArchive::extractTo``, it will be readable for everyone on the server.
64+
65+
The attacker runs again the command, but this time he runs the command on a directory created by the ``extractTo`` method:
66+
```
67+
appsec@server:$ ls /home/victim/public_html/extracted_here/
68+
```
69+
And everything will be listed. The attacker can also run ``cat`` on the files and expose the contents of the files.
70+
71+
A more elegant (and common) way to traverse between the extracted directories that are exposed will be by using a symlink technique.
72+
73+
## Reporting to PHP
74+
I reported this to PHP (bug *#79383* in PHP’s bug tracker) and they said that this is an expected behavior. However, they found out that it is **not documented anywhere.**
75+
76+
![screenshot1](./images/4.png)
77+
78+
As such, they agreed to add a warning (see below) saying that you should use ``umask()`` to prevent from any unnecessary info leakage:
79+
Commit: https://github.com/php/doc-en/commit/c870cfa314d880326cf30cd34b560c94f9526954
80+
81+
82+
![screenshot1](./images/5.png)
83+
84+
48.6 KB
Loading
8.48 KB
Loading
61.8 KB
Loading
80.6 KB
Loading
126 KB
Loading
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## CVE-2020-7066 – Null byte Poisoning in PHP7
2+
3+
When talking about Null-byte Poisoning in PHP, the most common scenario is file upload / file extension validation bypass (*CVE-2006-7243* and *CVE-2015-2348*). Those kinds of attacks are very rare because we left them in 2006-2015. However, there are other functions which are vulnerable to nullbyte poisoning and didn’t get enough attention / write-ups. The bug that I present here is relevant for all PHP versions as of March 2020.
4+
5+
Let's take this example: The PHP code allows sending requests only to ``.example.com`` subdomains.
6+
```php
7+
<?php
8+
$url = "http://localhost\0.example.com"; // payload
9+
10+
$host = parse_url($url, PHP_URL_HOST);
11+
if (substr($host, -12) !== '.example.com') { // pseudo-validation
12+
die('Not allowed');
13+
}
14+
$headers = get_headers($url);
15+
print_r($headers);
16+
?>
17+
18+
```
19+
20+
Output:
21+
The request will be sent to http://localhost even though the validation in the ``if`` statement is correct.
22+
```
23+
Array
24+
(
25+
[0] => HTTP/1.1 200 OK
26+
[1] => Date: Sun, 22 Mar 2020 21:19:03 GMT
27+
[2] => Server: Apache/2.4.7 (Unix)
28+
[3] => Spoofed-Info: isHere
29+
[4] => Connection: close
30+
[5] => Content-Type: text/html
31+
)
32+
```
33+
34+
## Root cause
35+
This is the source code of ``get_headers()`` :
36+
37+
![screenshot1](./vuln_1.png)
38+
39+
At line 672 the parameter is passed from the "php layer"(Zend engine) to the ``char *url`` pointer, and the length of the string is passed to ``size_t url_len``
40+
41+
At this point, the string length is **still** 29 bytes long(``http://localhost<NULL>.example.com`` and not ``http://localhost``)
42+
43+
If we debug this and set a breakpoint on ``zif_get_headers()`` we can see that we weren't able to fool Zend Engine. It knows the real length of the string, even with the null-byte injected in it:
44+
```
45+
(gdb) p url_len
46+
$2 = 29
47+
(gdb) x/29bc url
48+
0xb787c100: 104 'h' 116 't' 116 't' 112 'p' 58 ':' 47 '/' 47 '/' 108 'l'
49+
0xb787c108: 111 'o' 99 'c' 97 'a' 108 'l' 104 'h' 111 'o' 115 's' 116 't'
50+
0xb787c110: 0 '\000' 46 '.' 101 'e' 120 'x' 97 'a' 109 'm' 112 'p' 108 'l'
51+
0xb787c118: 101 'e' 46 '.' 99 'c' 111 'o' 109 'm'
52+
```
53+
54+
So, if PHP knows that the length of the string is 29 and not 16, how does this magic work?
55+
56+
If you look closely, at line 680 (in screenshot above) the ``url`` pointer is passed **but ``url_len`` is not**.
57+
When the execution reaches to ``php_stream_open_wrapper_ex()`` it doesn't know the length of the string so like any other typical C program, it will assume that the string ends in the first occurrence of a null byte.
58+
59+
## The Fix
60+
The git commit:
61+
https://github.com/php/php-src/commit/2bc92a0cf79e52e2096e45987468740d5bc748ed
62+
![screenshot1](./fix.png)
63+
64+
The PHP Development Team updated the parameter type to be a ``Z_PARAM_PATH`` (a macro that throws an error if there's a null-byte in the parameter) and not a ``Z_PARAM_STRING`` (which can represent strings with a null-byte in it)
65+
66+
Output:
67+
```
68+
PHP Warning: get_headers() expects parameter 1 to be a valid path, string given in ...
69+
```
70+
71+
72+
>**Note:** The bug was originally reported by ``64796c6e69 at gmail dot com`` (here: https://bugs.php.net/bug.php?id=79329). I wrote this to share a more in-depth analysis.
73+
74+
57.9 KB
Loading
Loading
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Out-of-Bounds read in ``urldecode()``
2+
3+
## Details
4+
* **Description**: "*In PHP versions 7.2.x below 7.2.30, 7.3.x below 7.3.17 and 7.4.x below 7.4.5, if PHP is compiled with EBCDIC support, ``urldecode()`` function can be made to access locations past the allocated memory, due to erroneously using signed numbers as array indexes.*" - In other words, if you type a negative hex value in a system with EBCDIC encoding enabled, you'll be able to leak memory ✨
5+
6+
* **CVSS3 Score**: 7.5 high
7+
8+
## Impact / Threat
9+
This bug requires the vulnerable server to enable EBCDIC encoding, so it mainly affects Mainframe systems(yuck!) **and** possibly some flavors of IBM Cloud Instances which runs PHP (IBM created this encoding so they have support in their cloud environment environment as well. It was found to be enabled in cloud "flavors" such as *Linux z/OS*. References: [[#1]](https://cloud.ibm.com/docs/runtimes/php?topic=PHP-getting_started), [[#2]](https://www.ibm.com/support/knowledgecenter/zosbasics/com.ibm.zos.zappldev/zappldev_14.htm), [[#3]](https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.gxla100/encodesupport.htm)).
10+
11+
12+
## The bug / Exploitation
13+
14+
This is the source code of ``urldecode()``:
15+
16+
[ext/standard/url.c](http://git.php.net/?p=php-src.git;a=blob;f=ext/standard/url.c;h=fe6d7f9de1d69eaafd577518d92d899feb7145b0;hb=fe6d7f9de1d69eaafd577518d92d899feb7145b0#l548) @ line 548
17+
18+
```c
19+
/* {{{ php_url_decode
20+
*/
21+
PHPAPI size_t php_url_decode(char *str, size_t len)
22+
{
23+
char *dest = str;
24+
char *data = str;
25+
26+
while (len--) {
27+
if (*data == '+') {
28+
*dest = ' ';
29+
}
30+
else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
31+
&& isxdigit((int) *(data + 2))) {
32+
#ifndef CHARSET_EBCDIC
33+
*dest = (char) php_htoi(data + 1);
34+
#else
35+
*dest = os_toebcdic[(char) php_htoi(data + 1)];
36+
#endif
37+
data += 2;
38+
len -= 2;
39+
} else {
40+
*dest = *data;
41+
}
42+
data++;
43+
dest++;
44+
}
45+
*dest = '\0';
46+
return dest - str;
47+
}
48+
49+
```
50+
51+
If ``CHARSET_EBCDIC`` is defined (usually, on systems with EBCDIC encoding support), an Out-of-Bounds Read can occur.
52+
53+
Let's focus on the relevant parts of the function:
54+
```c
55+
PHPAPI size_t php_url_decode(char *str, size_t len)
56+
{
57+
char *dest = str;
58+
char *data = str;
59+
/*...more code...*/
60+
61+
#ifndef CHARSET_EBCDIC
62+
*dest = (char) php_htoi(data + 1);
63+
#else
64+
*dest = os_toebcdic[(char) php_htoi(data + 1)]; // <--- oob read here
65+
#endif
66+
67+
/* ... more code ... */
68+
```
69+
* ``os_toebcdic[256]`` is an array(or a map, i assume) used for decoding purposes.
70+
* To convert the string input into hex values, PHP uses ``php_htoi()`` and then convert the result into a signed byte(char).
71+
* This signed number is then provided as an index to the ``os_toebcdic[]`` array.
72+
* There will be no OOB Read after the buffer because the max value of a byte is 0xff (==256), which is the same size as the ``os_toebcdic[]``
73+
* However, the casting (in the second bullet) is done to a ``char`` and not an ``unsigned char``, which means that we can insert **negative hex values** to leak values that are found in the memory **BEFORE** our buffer.
74+
75+
76+
77+
78+
payload:
79+
```php
80+
<?
81+
urldecode('%xfd'); //0xfd == -3, could be 0x80 for bigger OOB (which is -128 in dec, this is the minimum value of a signed byte. b10000000)
82+
?>
83+
```
84+
85+
in gdb:
86+
87+
```
88+
gdb-peda$ call php_htoi(data+1)
89+
$42 = 0xfd
90+
91+
gdb-peda$ p/d (char)$42
92+
$43 = -3
93+
```
94+
The array index is negative an memory will be leaked via the returned value of PHP's ``urldecode()``
95+
96+
## Understanding the Concept
97+
98+
Let's take the following example program:
99+
```c
100+
#include<stdio.h>
101+
102+
int main()
103+
{
104+
char text[255] = "ABCD";
105+
char buf[] = "QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ"; // not used in the program
106+
107+
char c = 0xfc; // signed char, can contain negative values (from -128 to 255)
108+
109+
printf("%x", text[c]); // text[-3]
110+
return 0;
111+
}
112+
```
113+
114+
We are using ``c`` as an index specifier to ``text`` and print the value in hex.
115+
116+
The expected output will be a character from the ``text[]`` array, maybe A(``0x41``) or B(``0x42``), or maybe C(``0x43``), etc. Even if the attacker will manage to read **after** ``D``, he will not be able to read the contents of what is after ``text[255]`` in memory (because the maximum size of ``char c`` is 255). So what do we do? insert a negative value :D
117+
118+
The actual output is: ``0x51``, which is Q, and belongs to anohter variable in the program (``buf``).
119+
It happens when an array index specifier is set to a negative value. In our case, ``c`` was set to ``0xfc`` (which is ``-3`` in dec)
120+
121+
In the following hexdump, you can see that ``buf`` is found right before our ``text[]`` array in the memory:
122+
```
123+
gdb-peda$ hexdump &buf
124+
125+
+0000 0xbffff4c5 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 51 │QQQQ│QQQQ│QQQQ│QQQQ│
126+
...
127+
+0020 0xbffff4e5 51 51 51 51 51 51 51 51 51 51 00 41 42 43 44 00 │QQQQ│QQQQ│QQ.A│BCD.│
128+
+0030 0xbffff4f5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
129+
pwndbg>
130+
+0040 0xbffff505 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │....│....│....│....│
131+
...
132+
```
133+
134+
Hence, we jumped "backwards" when we inserted negative values in the array index specifier.
135+
136+
## The Fix
137+
138+
The PHP Development team turned the array index specifier to be an ``unsigned char`` to avoid negative values in array index specifiers.
139+
140+
Same issue was found in ``php_raw_url_decode``
141+
142+
The commit: http://git.php.net/?p=php-src.git;a=blobdiff;f=ext/standard/url.c;h=1dd073e2bb423652821f351135b9582d76e175d5;hp=fe6d7f9de1d69eaafd577518d92d899feb7145b0;hb=9d6bf8221b05f86ce5875832f0f646c4c1f218be;hpb=14fcc813948254b84f382ff537247d8a7e5e0e62
143+
144+
![screenshot](./res/fix.png)
145+
146+
147+
>Original report #79465: https://bugs.php.net/bug.php?id=79465&edit=2
91.8 KB
Loading

0x04-0x05 [MapServer-CVEs]/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## About MapServer
2+
3+
https://github.com/mapserver/mapserver
4+
5+
>MapServer is a system for developing web-based GIS applications. The basic system consists of a CGI program that can be configured to respond to a variety of spatial requests like making maps, scalebars, and point, area and feature queries.
6+
7+
8+
>MapServer was originally written by Stephen Lime. Major funding for development of MapServer has been provided by NASA through cooperative argreements with the University of Minnesota, Department of Forest Resources.
9+
PHP/MapScript developed by DM Solutions Group.
10+
11+
12+
## PHP/MapScript Vulnerabillities :bug:
13+
As part of my PHP Internals research, I also learned about PHP **extensions**. I found a buffer overflow & format string vulnerabillities in a PHP extension called MapScript, which is part of the MapServer app.
14+
15+
In this directory, you'll find two of the reports I sent privately to MapServer development team with PoCs.
16+
17+
All of the findings were fixed and the project maintainers allowed me to disclose those reports after I sent a responsible, full disclosure request ( thanks Steve & Jeff :D )

0 commit comments

Comments
 (0)