Skip to content

Commit de0ea29

Browse files
committed
Updates php/challenge-89.md
Auto commit by GitBook Editor
1 parent 5f39299 commit de0ea29

File tree

9 files changed

+247
-9
lines changed

9 files changed

+247
-9
lines changed

php/challenge-80.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,44 @@
11
# Challenge
22
```php
3+
class FTP {
4+
public $sock;
35

6+
public function __construct($host, $port, $user, $pass) {
7+
$this->sock = fsockopen($host, $port);
8+
9+
$this->login($user, $pass);
10+
$this->cleanInput();
11+
$this->mode($_REQUEST['mode']);
12+
$this->send($_FILES['file']);
13+
}
14+
15+
private function cleanInput() {
16+
$_GET = array_map('intval', $_GET);
17+
$_POST = array_map('intval', $_POST);
18+
$_COOKIE = array_map('intval', $_COOKIE);
19+
}
20+
21+
public function login($username, $password) {
22+
fwrite($this->sock, "USER " . $username . "\n");
23+
fwrite($this->sock, "PASS " . $password . "\n");
24+
}
25+
26+
public function mode($mode) {
27+
if ($mode == 1 || $mode == 2 || $mode == 3) {
28+
fputs($this->sock, "MODE $mode\n");
29+
}
30+
}
31+
32+
public function send($data) {
33+
fputs($this->sock, $data);
34+
}
35+
}
436
```
537

638
# Solution
39+
This challenge contains two bugs that can be used together to inject data into the open FTP connection. The first bug is the usage of $_REQUEST in line 9 while only sanitizing $_GET and $_POST in lines 14 to 16. $_REQUEST is the combination of $_GET, $_POST, and $_COOKIE but it is only a copy of the values, not a reference. Therefore the sanitization of $_GET, $_POST, and $_COOKIE alone is not sufficient. A real world example of a vulnerability that is caused by a similar confusion can be found in our blog.
40+
41+
The second bug is the usage of the type-unsafe comparison == instead of === in line 25. This enables an attacker to inject and execute new commands in the existing connection, for example a delete command with the query string ?mode=1%0a%0dDELETE%20test.file.
742

843
# Refference
9-
+ php-security-calendar-2017
44+
+ php-security-calendar-2017 Day 16 - Poem

php/challenge-81.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,45 @@
11
# Challenge
22
```php
3+
class RealSecureLoginManager {
4+
private $em;
5+
private $user;
6+
private $password;
37

8+
public function __construct($user, $password) {
9+
$this->em = DoctrineManager::getEntityManager();
10+
$this->user = $user;
11+
$this->password = $password;
12+
}
13+
14+
public function isValid() {
15+
$pass = md5($this->password, true);
16+
$user = $this->sanitizeInput($this->user);
17+
18+
$queryBuilder = $this->em->createQueryBuilder()
19+
->select("COUNT(p)")
20+
->from("User", "u")
21+
->where("password = '$pass' AND user = '$user'");
22+
$query = $queryBuilder->getQuery();
23+
return boolval($query->getSingleScalarResult());
24+
}
25+
26+
public function sanitizeInput($input) {
27+
return addslashes($input);
28+
}
29+
}
30+
31+
$auth = new RealSecureLoginManager(
32+
$_POST['user'],
33+
$_POST['passwd']
34+
);
35+
if (!$auth->isValid()) {
36+
exit;
37+
}
438
```
539

640
# Solution
41+
This challenge is supposed to be a fixed version of day 13 but it introduces new vulnerabilities instead. The author tried to fix the DQL injection by applying addslashes() without substr() on the user name, and by hashing the password in line 13 using md5(). Besides the fact that md5 should not be used to hash passwords and that password hashes should not be compared this way, the second parameter is set to true. This returns the hash in binary format. The binary hash can contain ASCII characters that are interpreted by Doctrine. In this case an attacker could use the value 128 as the password, resulting in v�an���l���q��\ as hash. With the backslash at the end the single quote gets escaped leading to an injection. A possible payload could be ?user=%20OR%201=1-&passwd=128.
42+
To avoid DQL injections always use bound parameters for dynamic conditions. Never try to secure a DQL query with addslashes() or similar functions. Additionally, the password should be stored in a secure hashing format, for example BCrypt.
743

844
# Refference
9-
+ php-security-calendar-2017
45+
+ php-security-calendar-2017 Day 17 - Mistletoe

php/challenge-82.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
# Challenge
22
```php
3+
class JWT {
4+
public function verifyToken($data, $signature) {
5+
$pub = openssl_pkey_get_public("file://pub_key.pem");
6+
$signature = base64_decode($signature);
7+
if (openssl_verify($data, $signature, $pub)) {
8+
$object = json_decode(base64_decode($data));
9+
$this->loginAsUser($object);
10+
}
11+
}
12+
}
313

14+
(new JWT())->verifyToken($_GET['d'], $_GET['s']);
415
```
516

617
# Solution
18+
This challenge contains a bug in the usage of the openssl_verify() function in line 5 that leads to an authentication bypass in line 7. The function has three return values: 1 if the signature is correct, 0 if the signature verification failed, and -1 if there was an error while performing the verification. So if an attacker generates a valid signature for the data using another algorithm than the one pub_key.pem is using, the openssl_verify() function returns -1 which is casted to true automatically. To avoid this problem use the type-safe comparison === to validate the return value of openssl_verify(), or consider using a different library for cryptography.
719

820
# Refference
9-
+ php-security-calendar-2017
21+
+ php-security-calendar-2017 Day 18 - Sign

php/challenge-83.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
11
# Challenge
22
```php
3+
class ImageViewer {
4+
private $file;
35

6+
function __construct($file) {
7+
$this->file = "images/$file";
8+
$this->createThumbnail();
9+
}
10+
11+
function createThumbnail() {
12+
$e = stripcslashes(
13+
preg_replace(
14+
'/[^0-9\\\]/',
15+
'',
16+
isset($_GET['size']) ? $_GET['size'] : '25'
17+
)
18+
);
19+
system("/usr/bin/convert {$this->file} --resize $e
20+
./thumbs/{$this->file}");
21+
}
22+
23+
function __toString() {
24+
return "<a href={$this->file}>
25+
<img src=./thumbs/{$this->file}></a>";
26+
}
27+
}
28+
29+
echo (new ImageViewer("image.png"));
430
```
531

632
# Solution
33+
The ImageViewer class is prone to remote command execution through the size parameter in line 17. The preg_replace() call will purge almost any non-digit characters. This is not sufficient though because the function stripcslashes() will not only strip slashes but it will also replace C literal escape sequences with their actual byte representation. The backslash character is untouched by the preg_replace() call allowing an attacker to inject an octal byte escape sequence similar to 0\073\163\154\145\145\160\0405\073. The stripcslashes() function will evaluate this input to 0;sleep 5; which is concatenated into the system command and finally executed in the attackers favor.
734

835
# Refference
9-
+ php-security-calendar-2017
36+
+ php-security-calendar-2017 Day 19 - Birch

php/challenge-85.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,39 @@
11
# Challenge
22
```php
3+
set_error_handler(function ($no, $str, $file, $line) {
4+
throw new ErrorException($str, 0, $no, $file, $line);
5+
}, E_ALL);
36

7+
class ImageLoader
8+
{
9+
public function getResult($uri)
10+
{
11+
if (!filter_var($uri, FILTER_VALIDATE_URL)) {
12+
return '<p>Please enter valid uri</p>';
13+
}
14+
15+
try {
16+
$image = file_get_contents($uri);
17+
$path = "./images/" . uniqid() . '.jpg';
18+
file_put_contents($path, $image);
19+
if (mime_content_type($path) !== 'image/jpeg') {
20+
unlink($path);
21+
return '<p>Only .jpg files allowed</p>';
22+
}
23+
} catch (Exception $e) {
24+
return '<p>There was an error: ' .
25+
$e->getMessage() . '</p>';
26+
}
27+
28+
return '<img src="' . $path . '" width="100"/>';
29+
}
30+
}
31+
32+
echo (new ImageLoader())->getResult($_GET['img']);
433
```
534

635
# Solution
36+
This challenge contains a server-side request forgery vulnerability. It allows an attacker to perform requests on behalf of the attacked web server. Thus, servers can be reached that would otherwise be not reachable for an external attacker. For example, this can be abused to perform a port scan and to grab banners (e.g., version of the server) on an internal network that the web server is part of. The exploitable parts are the usage of file_get_contents() with unfiltered user input in line 14 and the printing of the error message to the user in line 23. An attacker can request an internal URI like ?img=http://internal:22 and would get a response such as failed to open stream: HTTP request failed! SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.2 if OpenSSH is running. Information like this can be used to prepare further attacks. Another popular exploit scenario is the retrieval of sensitive AWS credentials when attacking an AWS cloud instance. Besides that, filter_var() also accepts file:// URLs, enabling an attacker to load local files.
737

838
# Refference
9-
+ php-security-calendar-2017
39+
+ php-security-calendar-2017 Day 20 - Stocking

php/challenge-86.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,42 @@
11
# Challenge
22
```php
3+
declare(strict_types=1);
34

5+
class ParamExtractor {
6+
private $validIndices = [];
7+
8+
private function indices($input) {
9+
$validate = function (int $value, $key) {
10+
if ($value > 0) {
11+
$this->validIndices[] = $key;
12+
}
13+
};
14+
15+
try {
16+
array_walk($input, $validate, 0);
17+
} catch (TypeError $error) {
18+
echo "Only numbers are allowed as input";
19+
}
20+
21+
return $this->validIndices;
22+
}
23+
24+
public function getCommand($parameters) {
25+
$indices = $this->indices($parameters);
26+
$params = [];
27+
foreach ($indices as $index) {
28+
$params[] = $parameters[$index];
29+
}
30+
return implode($params, ' ');
31+
}
32+
}
33+
34+
$cmd = (new ParamExtractor())->getCommand($_GET['p']);
35+
system('resizeImg image.png ' . $cmd);
436
```
537

638
# Solution
39+
This challenge contains a command injection vulnerability in line 33. The developer declared strict_types=1 in line 1 to ensure the the type hint in the validate function in line 7 throws a TypeError exception if a non-int is passed to the class. Even with strict types enabled there is an bug with the usage of array_walk() which ignores the strict typing and uses the default weak typing of PHP instead. An attacker can therefore just append a command to the last parameter that is executed in the system call. A possible payload could look like ?p[1]=1&p[2]=2;%20ls%20-la.
740

841
# Refference
9-
+ php-security-calendar-2017
42+
+ php-security-calendar-2017 Day 21 - Gift Wrap

php/challenge-87.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
# Challenge
22
```php
3+
if (isset($_POST['password'])) {
4+
setcookie('hash', md5($_POST['password']));
5+
header("Refresh: 0");
6+
exit;
7+
}
38

9+
$password = '0e836584205638841937695747769655';
10+
if (!isset($_COOKIE['hash'])) {
11+
echo '<form><input type="password" name="password" />'
12+
. '<input type="submit" value="Login" ></form >';
13+
exit;
14+
} elseif (md5($_COOKIE['hash']) == $password) {
15+
echo 'Login succeeded';
16+
} else {
17+
echo 'Login failed';
18+
}
419
```
520

621
# Solution
22+
The code snippet suffers from 4 vulnerabilities. First, the type-unsafe comparison operator is used in line 12 to compare the hashed password to a string. The string has the form of the scientific notation and as a result it is interpreted as “zero to the power of X”, which is zero. So if we are able to generate a zero-string for the hashed user input as well, the check compares zero to zero and succeeds. This hashes are called “Magic Hashes” and a Google search reveals that the MD5 hash of the value 240610708 results in the desired properties. The code snippet calculates the MD5 hash of the password twice though, so it is not possible to directly submit the value. Instead you have to exploit the second vulnerability: the first hash is calculated on the server side but stored in a cookie on the client side. Thus the value 240610708 simply has to be directly injected into the password cookie.
23+
24+
There are two more vulnerabilities but they are not relevant for this challenge. First, the comparison of the hashes is vulnerable to timing attacks. To prevent this issue, the PHP function hash_equals() should be used for comparison. Second, the PHP function md5() is used to hash the password. The MD5 algorithm is considered broken and it was not designed for password hashing. Instead a secure password hashing algorithm like BCrypt should be used. It should be noted that passwords also should not be hard coded but separated into a configuration file.
725

826
# Refference
9-
+ php-security-calendar-2017
27+
+ php-security-calendar-2017 Day 22 - Chimney

php/challenge-88.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,47 @@
11
# Challenge
22
```php
3+
class LDAPAuthenticator {
4+
public $conn;
5+
public $host;
36

7+
function __construct($host = "localhost") {
8+
$this->host = $host;
9+
}
10+
11+
function authenticate($user, $pass) {
12+
$result = [];
13+
$this->conn = ldap_connect($this->host);
14+
ldap_set_option(
15+
$this->conn,
16+
LDAP_OPT_PROTOCOL_VERSION,
17+
3
18+
);
19+
if (!@ldap_bind($this->conn))
20+
return -1;
21+
$user = ldap_escape($user, null, LDAP_ESCAPE_DN);
22+
$pass = ldap_escape($pass, null, LDAP_ESCAPE_DN);
23+
$result = ldap_search(
24+
$this->conn,
25+
"",
26+
"(&(uid=$user)(userPassword=$pass))"
27+
);
28+
$result = ldap_get_entries($this->conn, $result);
29+
return ($result["count"] > 0 ? 1 : 0);
30+
}
31+
}
32+
33+
if(isset($_GET["u"]) && isset($_GET["p"])) {
34+
$ldap = new LDAPAuthenticator();
35+
if ($ldap->authenticate($_GET["u"], $_GET["p"])) {
36+
echo "You are now logged in!";
37+
} else {
38+
echo "Username or password unknown!";
39+
}
40+
}
441
```
542

643
# Solution
44+
The LDAPAuthenticator class is prone to an LDAP injection in line 24. By injecting special characters into the username it is possible to alternate the result set of the LDAP query. Although the ldap_escape() function is used to sanitize the input in lines 19 and 20, a wrong flag has been passed to the sanitize-calls resulting in insufficient/incorrect sanitization. Therefore, in this particular example, the LDAP injection results in an unauthenticated adversary bypassing the authentication mechanism by injecting the asterisk-wildcard * character as username and password to successfully login as an arbitrary user.
745

846
# Refference
9-
+ php-security-calendar-2017
47+
+ php-security-calendar-2017 Day 23 - Cookies

php/challenge-89.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
# Challenge
22
```php
3-
3+
@$GLOBALS=$GLOBALS{next}=next($GLOBALS{'GLOBALS'})
4+
[$GLOBALS['next']['next']=next($GLOBALS)['GLOBALS']]
5+
[$next['GLOBALS']=next($GLOBALS[GLOBALS]['GLOBALS'])
6+
[$next['next']]][$next['GLOBALS']=next($next['GLOBALS'])]
7+
[$GLOBALS[next]['next']($GLOBALS['next']{'GLOBALS'})]=
8+
next(neXt(${'next'}['next']));
49
```
510

611
# Solution
12+
This challenge consists of a code snippet that was created by one of our team members for the Hack.lu CTF Tournament. It makes heavy use of the next() function and the $GLOBALS array. The next() function moves the internal array pointer up by one. Combined with the $GLOBALS array this allows us to execute arbitrary code.
13+
The payload has to be split up into 2 segments: First, a PHP function to execute, passed in via $_COOKIE[‘GLOBALS’]. Second, parameters for the injected function, passed in via the file type of a sent file with the same name as the called PHP function. A more detailed write-up of the solution can be found here.
14+
15+
https://github.com/ctfs/write-ups-2014/tree/master/hack-lu-ctf-2014/next-global-backdoor
716

817
# Refference
918
+ php-security-calendar-2017

0 commit comments

Comments
 (0)