PHP手册查阅后のNewly discovered
今天把手册通过查找关键字把很多函数都翻了一遍,几乎用了一整天的时间,不过还是有一些新的函数发现的,比如读取文件的readgzfile
和代替var_dump和printf的debug_zval_dump
以及一大堆回调函数,对php的更能又有了一些新的了解,这里放一些手册查阅过程中看到的一些注意到的函数吧
获取请求头
getallheaders() — 获取全部 HTTP 请求头信息
getallheaders()['a'] - 获取请求头a的值
apache_request_headers() — 获取全部 HTTP 请求头信息
get_headers() — 取得服务器响应一个 HTTP 请求所发送的所有标头
header("HTTP/1.0 404 Not Found") — 发送原生 HTTP 头
外加一个变量在5.6.0就被废弃的$HTTP_RAW_POST_DATA
(后面的版本可以输出但是会给一个Warning
并且输出内容为null
同时有一点比较奇怪,我本地只有PHP5.3.29nts
我试了一下POST发送数据过去var_dump输出的还是null
回调函数callback
register_tick_function指定回调函数
<?php
declare(ticks=1);
register_tick_function('test', "ticker");
function test(argv){
echo "\nargv\n";
system('calc');
}
echo 1;
通过declare设置ticks时钟之后register_tick_function注册计数器调用的回调函数
此外还有一个函数:
register_shutdown_function — 注册一个会在php中止时执行的函数
一般回调函数
array_map — 为数组的每个元素应用回调函数
array_walk — 使用用户自定义函数对数组中的每个元素做回调处理
array_walk_recursive — 对数组中的每个成员递归地应用用户函数
array_udiff — 用回调函数比较数据来计算数组的差集
array_filter — 用回调函数过滤数组中的单元
array_reduce — 用回调函数迭代地将数组简化为单一的值
array_intersect_ukey — 用回调函数比较键名来计算数组的交集
array_intersect_uassoc — 带索引检查计算数组的交集,用回调函数比较索引
array_uintersect_assoc — 带索引检查计算数组的交集,用回调函数比较数据
array_uintersect_uassoc — 带索引检查计算数组的交集,用单独的回调函数比较数据和索引
filter_var_array — 获取多个变量并且过滤它们
filter_input_array — 获取一系列外部变量,并且可以通过过滤器处理它们
Collator::asort -- collator_asort — Sort array maintaining index association
filter_var_array — 获取多个变量并且过滤它们
filter_input_array — 获取一系列外部变量,并且可以通过过滤器处理它们
call_user_func
call_user_method
call_user_func_array
call_user_method_array
preg_filter
preg_replace_callback
preg_replace_callback_array
forward_static_call -- 限制是貌似只能在类里面的函数执行回调函数
unserialize_callback_func -- 如果解串行器发现有未定义类要被实例化,将会调用 unserialize() 回调函数,这个也没太多说明和示例,不过感觉有点特殊就记一下吧
header_register_callback — 调用一个 header 函数(但是其实就是直接将参数作为函数名直接调用无参方法)
只能说,array的数组处理在回调函数方面的调用函数是真的多
文件操作
写文件操作
DOMDocument::save
<?php
doc = new DOMDocument('1.0');
// we want a nice outputdoc->formatOutput = true;
root =doc->createElement('book');
root =doc->appendChild(root);title = doc->createElement('title');title = root->appendChild(title);
text =doc->createTextNode('This is the title');
text =title->appendChild(text);
echo 'Wrote:\'.doc->save("/tmp/test.xml") . ' bytes'; // Wrote: 72 bytes
?>
DOMDocument::saveHTMLFile
<?php
doc = new DOMDocument('1.0');
// we want a nice outputdoc->formatOutput = true;
root =doc->createElement('html');
root =doc->appendChild(root);head = doc->createElement('head');head = root->appendChild(head);
title =doc->createElement('title');
title =head->appendChild(title);text = doc->createTextNode('This is the title');text = title->appendChild(text);
echo 'Wrote: ' . $doc->saveHTMLFile("/tmp/test.html") . ' bytes'; // Wrote: 129 bytes
?>
FTP服务下载文件
<?php
// define some variables
local_file = 'local.zip';server_file = 'server.zip';
// set up basic connection
conn_id = ftp_connect(ftp_server);
// login with username and password
login_result = ftp_login(conn_id, ftp_user_name,ftp_user_pass);
// try to download server_file and save tolocal_file
if (ftp_get(conn_id,local_file, server_file, FTP_BINARY)) {
echo "Successfully written tolocal_file\n";
} else {
echo "There was a problem\n";
}
// close the connection
ftp_close($conn_id);
?>
这三个函数在可以在一些常用的文件操作被ban之后使用,当做一个备用参考吧,至于常用的写文件操作不想写了,网上很多了已经
读文件操作
加上文件名就能直接输出:
highlight_file — 语法高亮一个文件
show_source — 别名 highlight_file()
readfile — 输出文件
readgzfile - 输出打印gz文件,其实和readfile一样,可以直接输出index.php
需要资源但不需要输出函数:
gzpassthru — Output all remaining data on a gz-file pointer(可以直接输出index.php的文件资源)
将文件内需要echo或var_dump:
gzfile - 和file一样
file — 把整个文件读入一个数组中
file_get_contents — 将整个文件读入一个字符串
gzopen("index.php","r")打开的资源可以直接通过gzpassthru函数直接读出内容而不用var_dump,readgzfile更是可以直接输出文件
示例:
gzpassthru(gzopen("index.php","r"));
readgzfile("index.php",);
获取特殊字符
获取当前文件夹的.
session_get_cookie_params -- 获取会话 cookie 参数,第二个键值是/
phpversion — 获取当前的PHP版本
zend_version — 获取当前 Zend 引擎的版本
basename — 返回路径中的文件名部分
dirname — 返回路径中的目录部分
获取配置信息
ini_get_all — 获取所有配置选项
ini_get — 获取一个配置选项的值
get_cfg_var — 获取 PHP 配置选项的值
get_loaded_extensions -- 返回所有编译并加载模块名的 array
get_defined_constants -- 返回所有常量的关联数组,键是常量名,值是常量值
get_defined_functions — 返回所有已定义函数的数组
get_defined_vars — 返回由所有已定义变量所组成的数组
get_current_user — 获取当前 PHP 脚本所有者名称
ini_set — 为一个配置选项设置值
ini_alter — 别名 ini_set()
ini_restore — 恢复配置选
parse_ini_file — 解析一个配置文件 项的值
get_defined_vars配合var_dump直接输出全部当前环境的变量得到题目flag变量
命令执行
意外发现两个可以执行命令可以进行RCE但是可惜只能在PHP5.x的部分版本中使用,在php中均已被淘汰
phpdbg_exec — Attempts to set the execution context
php_check_syntax — 检查PHP的语法(并执行)指定的文件
dl — 运行时载入一个 PHP 扩展
runkit
runkit_lint — Check the PHP syntax of the specified php code
runkit_lint_file — Check the PHP syntax of the specified file
runkit_import — Process a PHP file importing function and class definitions, overwriting where appropriate
都可以达到include的效果不过可惜默认拓展情况下并不能执行这个函数
opcache_compile_file
opcache_compile_file — 无需运行,即可编译并缓存 PHP 脚本
不执行,直接编译后缓存,好像并没有什么用,并且默认拓展并不能执行这个函数
but:虽然没执行成功但是看手册的解释“以供后续请求调用”,有没有一种可能就是我们通过加载一个上传的文件,里面有一个RCE函数evil,先通过opcache_compile_file加载这个文件到缓存,然后我们在可控的地方执行index.php并没有定义的evil,然后这样子其实调用的是RCE的evil函数?(并不懂,只是看到注释的联想hh)
命令执行函数:
system — 执行外部程序,并且显示输出
passthru — 执行外部程序并且显示原始输出
exec — 执行一个外部程序
shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回
proc_open — 执行一个命令,并且打开用来输入/输出的文件指针
proc_terminate — 杀除由 proc_open 打开的进程
pcntl_exec — 在当前进程空间执行指定程序
pcntl_fork — 在当前进程当前位置产生分支(子进程)
popen — 打开进程文件指针
dl — 运行时载入一个 PHP 扩展
几个手册上有但是默认没有的debug函数(看看就好):
php_check_syntax — 检查PHP的语法(并执行)指定的文件
phpdbg_exec — Attempts to set the execution context
phpdbg_break_file — Inserts a breakpoint at a line in a file
phpdbg_break_next — Inserts a breakpoint at the next opcode
phpdbg_break_method — Inserts a breakpoint at entry to a method
phpdbg_break_function — Inserts a breakpoint at entry to a function
反序列化
Array::(un)serialize
查看手册的时候发现除了可以直接使用(un)serialize函数之外在Array数组类的内部也有(un)serialize函数,并且发现可以直接反序列化数据
var_dump(array().unserialize(serialize(new test())));
不过可惜Array内部调用(un)serialize也会受disabled_functions的限制所以并没有太大用处,
除了Array还有很多类也有(un)serialize函数,但是应该和Array一样还是会受到disable_functions限制,所以就不看了,这里方几个类内自己定义了(un)serialize函数的原生类:
ArrayIterator::serialize
ArrayIterator::unserialize
SplDoublyLinkedList::serialize
SplDoublyLinkedList::unserialize
SplObjectStorage::serialize
SplObjectStorage::unserialize
ArrayObject::serialize
ArrayIterator::unserialize
wddx_(un)serialize
除了Array的(un)serialize之外发现还有一个反序列化的函数,不过在7.4.0全部的wddx函数都被弃用了默认情况下使用不了
wddx_deserialize — Unserializes a WDDX packet
至于序列化的话可以用wddx_serialize_vars
也可以用wddx_serialize_values
函数
<?php
class test{
function __construct(){
this->name= "test";
}
function __wakeup(){
system('calc');
var_dump(this->name);
}
}
test=new test();
var_dump(wddx_deserialize(wddx_serialize_vars("test")));
var_dump(wddx_deserialize(wddx_serialize_value(test)));
?>
打印输出
如echo
,var_dump
,print
,scanf
都被ban了的话可以通过debug_zval_dump
输出包括数组在内的变量
debug_zval_dump
用起来感觉和var_dump
函数差不多
另外debug函数还有两个方便调试追踪的函数一起放一下吧:
-
debug_print_backtrace
-
debug_backtrace
此外看到array_rand
这个函数注意一下,因为相比next
,prev
,first
,current
这些只能获取下一个或者上一个又或者最后一个变量的函数相比,它是随机获取一个或多个变量,我们可以通过爆破结合get_headers函数和eval执行命令
协议
扒手册仔细看了一才发现原来php://
协议除了之前常用的几种之外还有几个伪协议
php://stdin
php://stdout
php://stderr
php://input
php://output
php://temp
php://memory
php://temp/maxmemory
php://fd
php://filter
除了php://协议之外的其它一些流协议:
ogg:// — 音频流
ftp:// -- ftps:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
rar:// — RAR
zlib:// -- bzip2:// -- zip:// — 压缩流
data:// — 数据(RFC 2397)
file:// — 访问本地文件系统
glob:// — 查找匹配的文件路径模式
http:// -- https:// — 访问 HTTP(s) 网址
phar:// — PHP 归档
ssh2:// — Secure Shell 2
expect:// — 处理交互式的流
data://text/plain;base64,
压缩流的几个协议:
compress.zlib://file.gz
compress.bzip2://file.bz2
zip://archive.zip#dir/file.txt