Дмитрий DarkByte Москин

Мой блог, да.
logo

Неподдерживаемый метод - RuCTF 2012 Quals

Опубликовано 19.03.2012 автором Дмитрий Москин
Неподдерживаемый метод [ctb] / 400 баллов
Неподдерживаемый метод.
Порт: 8080

Заходим браузером по указанному адресу и видим:
Error response
Error code 501.
Message: Unsupported method ('GET').
Error code explanation: 501 = Server does not support this operation.

Метод действительно не поддерживается.

Пробуем отправить POST запрос и получаем в ответ:
<?xml version='1.0'?>
<methodResponse>
<fault>
<value><struct>
<member>
<name>faultCode</name>
<value><int>1</int></value>
</member>
<member>
<name>faultString</name>
<value><string><class 'xml.parsers.expat.ExpatError'>:no element found: line 1, column 0</string></value>
</member>
</struct></value>
</fault>
</methodResponse>

Понимаем, что сервис ожидал получить данные в XML формате. Обращаем внимание на заголовок Server: BaseHTTP/0.3 Python/2.7.2+ и понимаем, что это XML-RPC.

Пишем скрипт для работы с XML-RPC и запрашиваем список поддерживаемых методов
<?
$method = 'system.listMethods';
$params = null;

$req = xmlrpc_encode_request($method, $params);
$cont = stream_context_create(array('http' => array(
'method' => "POST",
'header' => "Content-Type: text/xml",
'content' => $req
)));
$data = file_get_contents("http://10.0.0.3:8080/", false, $cont);

print_r(xmlrpc_decode($data));

И получаем в ответ:
Array
(
[0] => get_answer
[1] => get_server_var
[2] => system.listMethods
[3] => system.methodHelp
[4] => system.methodSignature
)

Запрашиваем ответ с помощью метода get_answer, но, увы, просто так отдавать нам его не хотят.
get_answer() 
-> get_answer() takes exactly 4 arguments (0 given)
get_answer(1,2,3,4)
-> Wrong parameters

С помощью метода get_server_var собираем всю полезную информацию про метод get_answer.
get_server_var('get_answer')
-> <function get_answer at 0x7fb515d096e0>
get_server_var('get_answer.func_code.co_consts')
-> ('Returns an answer if params are correct', 79011, 'Wrong parameters', 1702257177, 'The answer is %s')
get_server_var('get_answer.func_code.co_varnames')
-> ('a', 'b', 'c', 'd', 'key')

А затем получаем бинарный код функции get_answer.
get_server_var('get_answer.func_code.co_code') ->
fAAAfAEAF2QBAGsDAHIfAHQAAGQCAIMBAIIBAG4AAHwAAHwBAHwCABdrAwByPgB0AABkAgCDAQCC
AQBuAAB8AgB8AwAUZAMAawMAcl0AdAAAZAIAgwEAggEAbgAAdAEAfAAAgwEAdAEAfAEAgwEAF3QB
AHwCAIMBABd0AQB8AwCDAQAXfQQAZAQAfAQAFlM=

С помощью библиотеки dis в питоне декомпилируем код функции и получаем: get_answer.txt

Восстанавливаем код и получаем:
def get_answer(a, b, c, d):
if a + b != 79011:
raise ValueError('Wrong parameters')
if b + c != a:
raise ValueError('Wrong parameters')
if c * d != 1702257177:
raise ValueError('Wrong parameters')
key = (str(a) + str(b) + str(c) + str(d))
return 'The answer is %s' % key

Немного математической магии и получаем нужные числа: 66666, 12345, 54321, 31337.
get_answer(66666, 12345, 54321, 31337)
-> The answer is 66666123455432131337