Старый 12.10.2011, 17:11   #1
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию "Фишки" PHP

Чтобы не засорять кучу тем своими мелкими заметками, решил создать тему и перенести их все сюда.

Целочисленное переполнение в десериализации

При парсинге количества элементов (длины строки) результат накапливается в переменной result не зависимо от количества цифр и без проверок на переполнение.

File: /ext/standart/var_unserializer.c
Код:
static inline size_t parse_uiv(const unsigned char *p)
{
	unsigned char cursor;
	size_t result = 0;

	if (*p == '+') {
		p++;
	}
	
	while (1) {
		cursor = *p;
		if (cursor >= '0' && cursor <= '9') {
			result = result * 10 + (size_t)(cursor - (unsigned char)'0');
		} else {
			break;
		}
		p++;
	}
	return result;
}
POC

PHP код:
unserialize('s:4294967297:"a";'); 
unserialize('O:8:"stdClass":-2147483648:{}'); 


Возможность использовать произвольный символ вместо ; в десериализации строки

В том числе можно использовать в перечислении элементов объекта или массива.
Однако в объектах между элементами (если их больше 2) должен стоять ; или }
Таким образом можно нарушать логику самописных парсеров сериализованных данных (типа таких)

POC
Код:
	var_dump(
		unserialize('s:4:"test"X'),
		unserialize('a:2:{s:1:"a"Xs:1:"b";s:1:"c"Xs:1:"d"X}'),
		unserialize('O:8:"stdClass":2:{s:1:"a"Xs:1:"b"}s:1:"c"Xs:1:"d"X}')
	);


Неправильная замена символов в парсере переменных GET,POST,COOKIE

Можно нарушить логику парсера переменных символом [ без закрывающей скобки.
Так как после обнаружения [ останавливается замена точек и пробелов и пытается распарсить переменную как массив.

File: /main/php_variables.c
Код:
	for (p = var; *p; p++) {
		if (*p == ' ' || *p == '.') {
			*p='_';
		} else if (*p == '[') {
			is_array = 1;
			ip = p;
			*p = 0;
			break;
		}
	}
POC:
PHP код:
var_dump($_GET); 
Код:
http://php_test/var_parser.php?aa%20aa=1
array(1) { ["aa_aa"]=> string(1) "1" }
http://php_test/var_parser.php?aa.aa=1
array(1) { ["aa_aa"]=> string(1) "1" }
http://php_test/var_parser.php?aa[aa=1
array(1) { ["aa_aa"]=> string(1) "1" }


http://php_test/var_parser.php?a[a%20aa=1
array(1) { ["a_a aa"]=> string(1) "1" }
http://php_test/var_parser.php?a[a.aa=1
array(1) { ["a_a.aa"]=> string(1) "1" }
http://php_test/var_parser.php?a[a[aa=1
array(1) { ["a_a[aa"]=> string(1) "1" }

Последний раз редактировалось Jokester; 30.05.2012 в 13:54..
BlackFan вне форума   Ответить с цитированием
Старый 12.01.2012, 22:02   #2
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

Посмотрел на тему про смену пути в деструкторе и обнаружил вот такую фишку:

Изначальный скрипт
PHP код:
<?php
    
class {
        function 
__destruct() {
            echo 
"Destruct ".getcwd();
        }
    }
    
    echo 
"Main ".getcwd();
    
$a = new a;
    echo 
"test";    
?>
Результат:
Код:
Main Z:\home\test\www

test
Destruct Z:\usr\local\apache\bin
Логично, скрипт выполняется до конца, после чего вызываются деструкторы (в котором меняется путь).

Теперь изменим его
PHP код:
<?php
    
class {
        function 
__destruct() {
            echo 
"Destruct ".getcwd();
        }
    }
    
    echo 
"Main ".getcwd();
    new 
a;
    echo 
"test";    
?>
Результат:
Код:
Main Z:\home\test\www
Destruct Z:\home\test\www

test
В целом, тоже логично. Так как объект никуда не записывается, то он не нужен и вызывается деструктор, после чего скрипт продолжает работу.

Двигаемся дальше
PHP код:
<?php
    
class {
        function 
__destruct() {
            echo 
"Destruct ".getcwd()."\n";
        }
    }
    
    echo 
"Main ".getcwd()."\n";
    
$a unserialize(stripslashes($_GET['c']));
    echo 
"\ntest\n";    
?>
Код:
c=O:1:"a":0:{}
Main Z:\home\test\www

test
Destruct Z:\usr\local\apache\bin

c=O:1:"a":0:{X}
Main Z:\home\test\www
Destruct Z:\home\test\www
Notice:  unserialize() : Error at offset 12 of 13 bytes 

test
Следовательно, не смотря на то, что при десериализации возникла ошибка (из-за неправильного описания параметров), объект все равно создался.
Но десериализация вернула false и объект никуда не записался и его деструктор выполнился прямо в этом месте, а не в конце.

Продолжаем разговор
PHP код:
<?php
    
class {
        function 
__destruct() {
            echo 
"Destruct ".getcwd()."\n";
            echo 
"Test object params ".$this->X;
        }
    }
    
    echo 
"Main ".getcwd()."\n";
    
$a unserialize(stripslashes($_GET['c']));
    echo 
"\ntest\n";    
?>
Код:
c=O:1:"a":1:{s:1:"X";s:7:"TEST_OK";Z}

Main Z:\home\test\www
Destruct Z:\home\test\www
Test object params TEST_OK
Notice:  unserialize() : Error at offset 34 of 35 bytes 

test

Итог:
1) При вызове ошибки в описании параметров объекта в десериализации, он все равно создатся и у него выполнится деструктор
2) Так как при ошибке unserialize вернет false, то объект станет никому не нужным и его деструктор вызовется сразу после десериализации. То есть мы можем контролировать в какой именно точке вызвать деструктор.
3) При ошибке в описании параметров в десериализации все ранее описанные параметры нормально срабатывают.

Пригодится, если в деструкторе работают с чем-нибудь глобальным (переменные, файлы и тп.)

Последний раз редактировалось BlackFan; 12.01.2012 в 22:06..
BlackFan вне форума   Ответить с цитированием
Старый 27.03.2012, 18:11   #3
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

Во-первых, вот в этой статейке я ошибся, когда описывал десериализацию класса, который реализует интерфейс Serializable.

Код:
C:9:"testClass":4:{i:1;}
[тип]:[длина_названия]:[название]:[длина_сериализованных_данных]:{[сериализованные_данные];}
PHP код:
class testClass implements Serializable{
    private 
$data;

    public function 
serialize() {
        return 
serialize($this->data);
    }

    public function 
unserialize($data) {
        
$this->data unserialize($data);
    }

Десериализация C:9:"testClass":4:{i:1;} приведет к созданию объекта "testClass" и вызова у него функции unserialize с параметром "i:1;". Ну и сериализация приведет к вызову функции serialize.

===========

Если неправильно задать название переменной, то при попытке определить ее модификатор доступа (public, private и protected) в функциях var_dump и get_object_vars (вроде еще в каких-то) это приведет к Notice.

PHP код:
    class testClass {}
    
var_dump(unserialize('O:9:"testClass":1:{S:2:"\00a";i:1;}')); 
Код:
Notice: Illegal member variable name in /blah/blah/test.php on line 4
  [" a"]=>
  int(1)
}
PHP код:
    class testClass {}
    
get_object_vars(unserialize('O:9:"testClass":1:{S:3:"\00a\00";i:1;}')); 
Код:
Notice: Corrupt member variable name in /blah/blah/test.php on line 4

Последний раз редактировалось Jokester; 30.05.2012 в 13:57..
BlackFan вне форума   Ответить с цитированием
Старый 27.03.2012, 21:45   #4
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

При наличие нескольких переменных с одинаковыми именами в _GET и _POST результирующей будет последняя.
Для _COOKIE в коде исключение и толстый комментарий:
Код:
/* 
 * According to rfc2965, more specific paths are listed above the less specific ones.
 * If we encounter a duplicate cookie name, we should skip it, since it is not possible
 * to have the same (plain text) cookie name for the same path and we should not overwrite
 * more specific cookies with the less specific ones.
 */
В итоге результирующим идет первая кука, однако реализовано это неправильно (thx Jokester)
Код:
Cookie: a=1; a=2; 
array(1) {
  ["a"]=>
  string(1) "1"
}

Cookie: a=1; a[]=2;
array(1) {
  ["a"]=>
  array(1) {
    [0]=>
    string(1) "2"
  }
}
BlackFan вне форума   Ответить с цитированием
Старый 30.03.2012, 00:36   #5
BlackFan
 
Аватар для BlackFan
 
Регистрация: 08.07.2010
Сообщений: 354
Репутация: 402
По умолчанию

Хотел обойти open_basedir...
Идея была в следущем:
1) Найти функцию, которая загружает файл в обход basedir, но из пхп не дает напрямую работать с содержимым файла (что-нибудь типа createimagefrompng, в таких функциях больше вероятность отсутствия проверки).
2) Зарегистировать свой filter
3) Загрузить с использованием своего фильтра

Судя по всему идея была фейловая и open_basedir проверяется именно на моменте разбора фильтров, да и функций поддерживающих php-wrapper маловато, но тем не менее как побочный выхлоп:

Чтение файлов через функции изначально не рассчитанные на это или требующие определенный формат файла

PHP код:
class readfile_filter extends php_user_filter {
  function 
filter($in$out, &$consumed$closing)
  {
    while (
$bucket stream_bucket_make_writeable($in)) {
      echo 
$bucket->data;
      
$consumed += $bucket->datalen;
      
stream_bucket_append($out$bucket);
    }
    return 
PSFS_PASS_ON;
  }
}
    
stream_filter_register("readfile""readfile_filter");

@
parse_ini_file('php://filter/read=readfile/resource=test1.txt');
@
simplexml_load_string('php://filter/read=readfile/resource=test1.txt'); 

Последний раз редактировалось BlackFan; 30.03.2012 в 00:42..
BlackFan вне форума   Ответить с цитированием
Старый 22.06.2012, 15:27   #6
Beched
 
Регистрация: 06.07.2010
Сообщений: 400
Репутация: 118
По умолчанию

Не понимаю, почему в теме нет комментариев и интереса к ней =(

На мой взгляд, самая перспективная в плане использования фишка -- это пропихивание произвольных символов в ключи массивов GPC.

Прогрепал некоторые популярные движки по слову foreach. Смотрел не очень внимательно, обращал внимание лишь на циклы вида foreach( $SMTH_FROM_GPC as $k => $v ).

Ничего серьёзного вроде не нашёл, кроме каких-то мелких хссок даже не помню где и вроде скули в модерке вбсео.

Но, думаю, стоит посмотреть внимательнее. Разработчики булки умные -- они фильтруют и ключи массивов, не только значения.
Можно попробовать совместить с динамикой, пофаззить.
Beched вне форума   Ответить с цитированием
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход



Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot