【BUUCTF】 个人记录——web

目录


web

[HCTF 2018]WarmUp

打开开发者模式,发现有个source.php

访问一下

还有个hint.php,也访问一下

还是来审计一下source.php的代码:

如果参数page已经设置且page为字符串,满足条件,第一个if语句不执行

如果page的值在白名单中,执行第二个if语句,返回true

先在page后加上一个 ? ,再用mb_strpos返回page中首次出现问号的位置,然后用mb_substr截取从0开始到第一个问号位置为止的字符串,将其赋值为_page

如果_page在白名单中(hint.php或者source.php),执行第三个if语句,返回true

将page进行url解码,赋值给_page

之后同样进行截取操作,如果在白名单中执行第四个if语句返回true

如果三个if语句都没能返回true,则返回false和显示you can't see it

下面的if判断语句,输入不为空,是字符串,且emm类返回true就能包含写入的file执行request(文件包含)

第二个if、语句直接判断page在不在白名单,不能用

确保第一个问号之前是source.php或者hint.php即可通过,返回true

所以payload:/source.php?file=source.php?/../../../../ffffllllaaaagggg

逻辑大概是这样的:

通过GET提交的url:file=source.php?../../../../../../ffffllllaaaagggg被编码为source.php%3f..%2f..%2f..%2f..%2f..%2f..%2fffffllllaaaagggg,里面没有了问号,自然通过了第一次问号截断,还是source.php%3f..%2f..%2f..%2f..%2f..%2f..%2fffffllllaaaagggg,之后进行url解码,变回file=source.php?../../../../../../ffffllllaaaagggg,问号截断是source.php,通过检查

这里之所以可以包含到ffffllllaaaagggg是因为PHP将 source.php?/ 视作了一个文件夹,然后 ../ 的用途是返回上级目录

ffffllllaaaagggg位于根目录下,一般Web服务的文件夹在/var/www/html目录中,再加上source.php?/这个文件夹,所以总共需要../四次来返回到根目录

不知道具体位置情况下就需要一层层试了

疑问

  1. 把payload最前面的source.php换成index.php也能得到flag,好像是说source.php是index.php的源代码?

  2. 第三个语句截取'?'前部分,由于?被后部分被解析为get方式提交的参数,也不可利用
    第四个if语句中,先进行url解码再截取,因此我们可以将?经过两次url编码,在服务器端提取参数时解码一次,checkFile函数中解码一次,仍会解码为'?',仍可通过第四个if语句校验。

这是王叹之师傅的分析,有点没看明白使用第四个if的原因

在构造payload时并没有使用编码后的 ? ,二次编码的 ? 是%235f,使用 ? 和 %235f都能拿到flag

扩展

PHP isset函数

bool isset ( mixed $var [, mixed $... ] )

检测变量是否设置,并且不是 null

如果指定变量存在且不为 null,则返回 true,否则返回 false

参数 描述
var 要检查的变量

PHP is_string函数

bool is_string ( mixed $var )

检测变量是否是字符串

如果指定变量是字符串则返回 true,否则返回 false

参数 描述
var 要检查的变量

PHP in_array函数

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

搜索数组中是否存在指定的值

参数 描述
needle 必需。规定要在数组搜索的值
haystack 必需。规定要搜索的数组
strict 可选。如果该参数设置为 TRUE,则 in_array() 函数检查搜索的数据与数组的值的类型是否相同

PHP mb_substr函数

mb_substr ( string $str , int $start [, int $length = NULL [, string $encoding = mb_internal_encoding() ]] ) : string

返回字符串的一部分,字符串可以包括中文(如果是sustr函数则不能含有中文)

参数 描述
str 必需。从该 string 中提取子字符串
start 必需。规定在字符串的何处开始
正数——在字符串的指定位置开始
负数——在从字符串结尾的指定位置开始
0——在字符串中的第一个字符处开始
length 可选。规定要返回的字符串长度。默认是直到字符串的结尾
正数——从 start 参数所在的位置返回
负数——从字符串末端返回
encoding 可选。字符编码。如果省略,则使用内部字符编码

PHP mb_strpos函数

mb_strpos ( string $haystack , string $needle [, int $offset = 0 [, string $encoding = mb_internal_encoding() ]] )

查找字符串在另一个字符串中首次出现的位置

参数 描述
haystack 必需。被查找的字符串
needle 必需。在haystack中查找这个字符串,和 strpos() 不同的是,数字的值不会被当做字符的顺序值
length 可选。搜索位置的偏移。如果没有提供该参数,将会使用 0。负数的 offset 会从字符串尾部开始统计
encoding 可选。字符编码。如果省略,则使用内部字符编码

PHP urldecode函数

urldecode ( string $str ) : string

解码给出的已编码字符串中的任何 %##。 加号 + 被解码成一个空格字符

参数 描述
str 要解码的字符串

PHP empty函数

bool empty ( mixed $var )

用于检查一个变量是否为空

当 var 存在,并且是一个非空非零的值时返回 false 否则返回 true

参数 描述
var 待检查的变量

PHP include与require语句

include 'filename';
require 'filename';

包含并运行指定文件

在服务器执行 PHP 文件之前在该文件中插入一个文件的内容

区别:

require 一般放在 PHP 文件的最前面,程序在执行前就会先导入要引用的文件;引入的文件有错误时,执行会中断,并返回一个致命错误

include include 一般放在程序的流程控制中,当程序执行时碰到才会引用,简化程序的执行流程;引入的文件有错误时,会继续执行,并返回一个警告


[强网杯 2019]随便注

从网页标签还有粗体大字已经知道是sql注入了

常规试探



是php代码啊

先测一下几个字段,3显示不存在,2有回应

联合查询一下

这下是php的preg_match函数,匹配正则表达式的,括号里面的全部不能用

想试试看能不能用双写之类的绕过,发现不行后百度了一下这个函数的绕过

preg_match只能处理字符串,当传入的subject是数组时会返回false

还是不会,学习wp

这题的考点是堆叠注入

查询数据库

查询表

查询words

查询1919810931114514

观察两个表,words表有id和data两个字段,默认查询的时候查出来的也是两个字段,说明查的是words表,而1919810931114514表中只有一个字段,但是flag在这个表中

方法一:修改表的名字

将表words改为word,表1919810931114514名字改为words,,那么就能得到flag的内容了。

1 ' ; rename tables `words` to `word`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100) ; #

再用1' or 1='1拿到flag

方法二:预编译

-1';set @sql = CONCAT('se','lect * from `1919810931114514`;');prepare stmt from @sql;EXECUTE stmt;#

检测到set和prepare在$inject里面,大写绕过

-1';Set @sql = CONCAT('se','lect * from `1919810931114514`;');Prepare stmt from @sql;EXECUTE stmt;#

扩展

堆叠注入

即将sql语句堆叠在一起进行查询,用;分隔语句,能同时查询多条语句

原理是php中mysql_multi_query()函数支持多条sql语句同时执行,如果后台使用的是mysqli_query()函数,那么堆叠注入就失效了

PHP preg_match函数

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

搜索subject与pattern给定的正则表达式的一个匹配。

参数 描述
$pattern 要搜索的模式,字符串形式
$subject 输入字符串
$matches 如果提供了参数matches,它将被填充为搜索结果。$matches[0]将包含完整模式匹配到的文本, $matches[1] 将包含第一个捕获子组匹配到的文本,以此类推
$offset 通常,搜索从目标字符串的开始位置开始。可选参数 offset 用于 指定从目标字符串的某个未知开始搜索(单位是字节)

反引号

反引号可作为分隔符及注释,在遇到数字表名/库名的时候需要用反引号括起来,字母表名/库名则不需要

在遇到mysql保留词是表名/库名时,需要用反引号加以区别,否则mysql无法识别

预编译

实际上被用来防止SQL注入,SQL注入的本质是未将数据与代码有效区分开,预编译的目的就在于解决这一点。

常见的有关防止SQL注入的预编译手段都是基于后端代码的,例如PHP或Java的PDO,这次是使用SQL命令直接预编译语句

给sql赋值

set @sql = CONCAT('se','lect * from `1919810931114514`;');

定义编译语句

prepare stmt from @sql;

执行编译语句

EXECUTE stmt;

[极客大挑战 2019]Havefun

在开发者工具界面,有段被注释的php代码

用get提交cat,如过输入的cat=dog,那就得到Syc{cat_cat_cat_cat}

扩展

URL的特殊符号

名称 含义 十六进制码
+ 表示空格 %2B
空格 等效于+号 %20
/ 分隔目录和子目录 %2F
? 分隔实际的URL和查询内容 %3F
% 指定特殊字符 %25
# 网页中的一个位置,其右面的字符,就是该位置的标识符 %23
& 不同参数间的间隔符 %26
= 参数的值 %3D

[SUCTF 2019]EasySQL

稍微试了一下 unionororderbyandfromflag 被过滤了,双写和大小写都不行

在输入1的情况下会显示一点点代码

试试看堆叠注入

堆叠注入能行,查表

之后无论是查flag还是1都不行,因为from被过滤了。。。

束手无策,学习wp

本题代码是

select $_GET['query'] || flag from flag

方法一:

1;set sql_mode=PIPES_AS_CONCAT;select 1

组合上去的代码就成了

select 1;set sql_mode=PIPES_AS_CONCAT;select 1 || flag from Flag

PIPES_AS_CONCAT能把 || 视为连接符而不是运算符

方法二:

*,1

组合成的代码是

select *,1 || flag from flag

扩展

常用的sql_mod

名称 作用
ONLY_FULL_GROUP_BY 对于GROUP BY聚合操作。如果在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,因为列不在GROUP BY从句中
NO_AUTO_VALUE_ON_ZERO 该值影响自增长列的插入。默认设置下,插入0或NULL代表生成下一个自增长值。如果用户希望插入的值为0,而该列又是自增长的,那么这个选项就派上用场了
STRICT_TRANS_TABLES 在该模式下,如果一个值不能插入到一个事务中,则中断当前的操作,对非事务表不做限制
NO_ZERO_IN_DATE 在严格模式下,不允许日期和月份为零
NO_ZERO_DATE 设置该值,mysql数据库不允许插入零日期,插入零日期会抛出错误而不是警告
ERROR_FOR_DIVISION_BY_ZERO 在insert或update过程中,如果数据被零除,则产生错误而非警告。如果未给出该模式,那么数据被零除时Mysql返回NULL
NO_AUTO_CREATE_USER 禁止GRANT创建密码为空的用户
NO_ENGINE_SUBSTITUTION 如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常
PIPES_AS_CONCAT 将"||"视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样,也和字符串的拼接函数Concat相类似
ANSI_QUOTES 启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符

[ACTF2020 新生赛]Include

蒙了,找了一圈没看到哪里有什么注释之类的东西

学习wp,本题是文件包含

过滤了input,但是没有过滤filter

把得到的base64编码解码就行

扩展

PHP伪协议

php://

访问输入输出流,有两个常用的子协议

php://input

将要执行的语法php代码写在post中提交,不用键与值的形式,只写代码即可

php://filter

设计用来过滤筛选文件

非php语法文件include失败,输出源码内容

php语法文件include成功,直接运行

因此想要读取php文件的源码,需要先base64编码,再传入include函数,这样就不会被认为是php文件,从而输出base64编码,之后对编码解码即可

最常用的payload
?file=php://filter/read=convert.base64-encode/resource=flag.php

file://

与php://filter类似,访问本地文件,但只能传入绝对路径

phar://

查找指定压缩包内的文件,相对路径与绝对路径均可

zip://

用法与phar类似,不过只能传入绝对路径,而且要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23


[极客大挑战 2019]Secret File

主界面什么按钮都没有,打开开发者模式,看到了一个php界面

点SECRET

说我没看清,估计是重定向跳转了

那我抓包看,嗯,是302跳转

前往secr3t.php

再去flag.php

。。。。。这抓包也没有用,整个页面就这几句话,没有flag

仔细想想,secr3t.php显示出来的不是这样的黑色页面而是php代码,这题也是文件包含

代码中用stristr函数ban掉了 ../tpincludedata,那还能用filter

构造payload输入后直接显示了base64编码(特别长)

解码

扩展

PHP stristr() 函数

字符串在另一字符串中的第一次出现,并返回字符串的剩余部分(相当于可以用作过滤关键词,导致语句不完整)

stristr(string,search,before_search)
参数 描述
string 必需。规定被搜索的字符串
search 必需。规定所搜索的字符串。如果该参数是数字,则搜索匹配该数字对应的 ASCII 值的字符
before_search 可选。一个默认值为 "false" 的布尔值。如果设置为 "true",它将返回 search 参数第一次出现之前的字符串部分

[ACTF2020 新生赛]Exec

完全懵逼,只知道ping和测网络延迟有关

学习wp去

本题是命令执行

开发者模式下啥也没有,直接开始尝试

ping命令执行成功了,那就尝试能否用管道符连接另一个命令

用ls发现有个index.php

index.php就是源码啊。。。

没有过滤那就找flag

扩展

Ping

ping (Packet Internet Groper)是一种因特网包探索器,用于测试网络连接量的程序。Ping 是工作在TCP/IP网络体系结构中应用层的一个服务命令,主要是向特定的目的主机发送 ICMP(Internet Control Message Protocol 因特网报文控制协议)Echo 请求报文,测试目的站是否可达及了解其有关状态

管道符

|(就是按位或),直接执行|后面的语句

||(就是逻辑或),如果前面命令是错的那么就执行后面的语句,否则只执行前面的语句

&(就是按位与),&前面和后面命令都要执行,无论前面真假

&&(就是逻辑与),如果前面为假,后面的命令也不执行,如果前面为真则执行两条命令

; 前后都执行,无论前面真假,同&,(linux也有)

Linux ls 命令

 ls [-alrtAFR] [name...]

用于显示指定工作目录下之内容(列出目前工作目录所含之文件及子目录)

参数 描述
-a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
-r 将文件以相反次序显示(原定依英文字母次序)
-t 将文件依建立时间之先后次序列出
-A 同 -a 但不列出 "." (目前目录) 及 ".." (父目录)
-F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
-R 若目录下有文件,则以下之文件亦皆依序列出

Linux cat 命令

cat [-AbeEnstTuv] [--help] [--version] fileName

用于连接文件并打印到标准输出设备上

参数 描述
-n 或 --number 由 1 开始对所有输出的行数编号
-b 或 --number-nonblank 和 -n 相似,只不过对于空白行不编号
-s 或 --squeeze-blank 当遇到有连续两行以上的空白行,就代换为一行的空白行
-v 或 --show-nonprinting 使用 ^ 和 M- 符号,除了 LFD 和 TAB 之外
-E 或 --show-ends 在每行结束处显示 $
-T 或 --show-tabs 将 TAB 字符显示为 ^I
-A, --show-all 等价于 -vET
-e 等价于"-vE"选项
-t 等价于"-vT"选项

[GXYCTF2019]Ping Ping Ping

嗯?

去学wp

稍微看了一点,原来 /?= 是get提交

常规ping命令能正常返回

用下ls,需要注意的是不能用空格,否则会显示 fxck your space。。。。

然后就出现问题了,cat语句里面必有空格,需要linux的空格绕过,sql的那套不行

url自动变成的%20不行,${IFS}不行,$IFS$9成功了,但是flag还是拿不到

去看看index.php

过滤了空格,bash,而且严格过滤flag,至少双写是不行的

又不会了,再去看看wp

常规方法

$a可以进行覆盖,payload用 ; 连接

在开发者模式中拿到flag

payload的意思是构造一个变量a=g,正则匹配是按照flag的顺序在匹配,由于匹配的时候只看到了fla,这样就绕过了过滤,之后在解析$a=g,语句就变成了:

/?ip=127.0.0.1;a=g;cat$IFS$1flag.php

于是就得到了flag

base64编码方法

禁用bash那就用sh,bash的大部分脚本能在sh运行

Y2F0IGZsYWcucGhw 是cat flag.php的base64编码,-d执行

内联方法

内联是将反引号内命令的输出作为输入执行

反引号里面的系统命令会先执行,成功执行后将结果传递给调用它的命令

疑问

  1. 为什么$IFS$1就能绕过?

  2. $a=shell_exec("ping -c 4 ".$ip);这句代码的意义是什么?

扩展

Linux空格绕过方法

%20 //空格
%09 //tab键
${IFS} //$IFS(Internal Field Separator,内部字段分隔符的缩写)
$IFS$1 //非0的数都行
{cat,flag.php} //用逗号实现了空格功能
<和<>重定向符

PHP shell_exec函数

通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回

shell_exec ( string $cmd ) : string
参数 描述
cmd 要执行的命令

[极客大挑战 2019]Knife

白给的shell,菜刀,post提交Syc(PHP的一句话木马),感觉暗示的很明显

拿蚁剑连一下

翻到根目录,找到flag

扩展

菜刀与蚁剑的基本原理

利用文件上传漏洞,往目标网站传入一句话木马,控制部分甚至全部权限(php,asp,aspx等脚本语言)

一句话木马

简称Webshell

常见一句话木马:

php: <?php @eval($_POST['cmd']);?>
asp: <%eval request ("cmd")%>
aspx:<%@ Page Language="Jscript"%> <%eval(Request.Item["cmd"],"unsafe");%>
javascript:<script language = 'php'>@eval($_POST[cmd]);</script>

@表示后面即使执行错误,也不报错。

eval()函数表示括号内的语句字符串什么的全都当做代码执行。

$_POST['attack']表示从页面中获得attack这个参数值。


[护网杯 2018]easy_tornado

有三个txt文件

flag.txt给出了flag在fllllllllllllag

welcome.txt只有一个单词

嗯?

hints.txt给出了一个md5的构成方法

结合三个txt文件的ur构成都是/file?filename=/xxx.txt&filehash=xxx

之后就想着抓包拿cookie,对fllllllllllllag进行md5加密,连起来编码(+,空格都试过了),然而并不对

无奈,学下wp

标题是tornado,也就是模板注入

出现error也证明存在注入点

而报错页面显示的url给出了参数是msg

这样就找到了回显点

Tornado提供了一些对象别名来快速访问对象

查阅Tornado文档,找到了有关cookie_secret的描述(所以我之前把它当作cookie是大错特错)

cookie_secret在Application对象settings属性中

RequestHandler.settings有一个别名:self.application.settings

handler指向的处理当前这个页面的RequestHandler对象,
RequestHandler.settings又指向self.application.settings,
因此handler.settings指向RequestHandler.application.settings。

这样得到了cookie_secret

接下来就是md5编码了

然后我又错了,这里文件名不是fllllllllllllag而是/fllllllllllllag

双错了,url中文件名没有.txt

叒错了,hint里面那个 + 是连接在一起的意思,不能作为编码的一部分

下面这个才是对的流程

扩展

SSTI

SSTI即服务器端模板注入(也就是模板注入)

主流的模板引擎

语言类型 模板引擎
Java Velocity、Freemarker、Pebble、Thymeleaf、Jinjava和StringTemplate
PHP Twig、Smarty、Dwoo、Volt、Blade、Plates和LATTE
Python Jinja2、Tornado和Mako

决策树

James Kettles提出的决策树,可以用来识别所使用的模板。

这个决策树是由简单的评估组成的,其中的表达式无法适用于每一种技术。

但是由于这些都是非常基本的表达式,所以当一个模版库的新版本发布时,这些表达式也不会很快变得过时。


[RoarCTF 2019]Easy Calc

开发者模式下有段代码

尝试无果,学习wp

calc.php可以访问,得到了源码

WAF禁止了传入字母,但是利用PHP的字符串解析特性绕过WAF

直接在url里面输入,在字母前加入一个空格

扫根目录下的所有文件,由于 / 被过滤了,就得用chr()绕过( /=chr(47) )

因为是扫描根目录,url肯定是以 / 分隔的,所以可以扫描以 / 开头的文件

打开f1agg( f=chr(102),1=chr(49),a=chr(97),g=chr(103)) )

扩展

WAF

WAF就是web应用防护系统(Web Application Firewall)

WAF是看不到的,而且WAF的语言不一定和题目一致

PHP的字符串解析特性

PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:

  1. 删除空白符
  2. 将某些字符转换为下划线(包括空格)

因为本题并没有过滤"空格+字母",所以" num"能通过WAF检测,PHP在读取时删除了空格,就完成了绕过

PHP scandir() 函数

scandir(directory,sorting_order,context);

返回指定目录中的文件和目录的数组

参数 描述
directory 必需。规定要扫描的目录
sorting_order 可选。规定排列顺序。默认是 0,表示按字母升序排列。如果设置为 SCANDIR_SORT_DESCENDING 或者 1,则表示按字母降序排列。如果设置为 SCANDIR_SORT_NONE,则返回未排列的结果
context 可选。规定目录句柄的环境。context 是可修改目录流的行为的一套选项

PHP echo() 函数 与 print() 函数

echo(strings)
&
print(strings)

输出一个或多个字符串(以及整型和int型浮点型数据,不能打印复合型和资源型数据)

函数名 参数 描述 区别
echo strings 必需。发送到输出的一个或多个字符串 可以连续输出多个变量。不能把打印的值赋给变量,它没有像函数的行为,不能用于函数的上下文
print strings 必需。发送到输出的一个或多个字符串 只能一次输出一个变量。打印的值能直接复制给一个变量,如 $a = print “123”

echo() 函数比 print()函数速度稍快

两个函数都不是严格意义上的函数,他们是 语言结构

PHP var_dump() 函数 与 print_r() 函数

void var_dump ( mixed $expression [, mixed $... ] )
&
bool print_r ( mixed $expression [, bool $return ] )

var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构

print_r() 函数用于打印变量,以更容易理解的形式展示

函数名 参数 描述 区别
var_dump $expression 需要输出的变量 能打印复合类型的数据,还能打印资源类型的变量
print_r $expression 需要输出的变量。如果给出的是 string、integer 或 float 类型变量,将打印变量值本身。如果给出的是 array,将会按照一定格式显示键和元素 只能打印一些易于理解的信息,且在打印数组时,会将把数组的指针移到最后边,使用 reset() 可让指针回到开始处
$return 可选,如果为 true 则不输出结果,而是将结果赋值给一个变量,false 则直接输出结果

var_dump()输出的信息则比较详细

PHP file_get_contents() 函数

file_get_contents(path,include_path,context,start,max_length)

把整个文件读入一个字符串中

参数 描述
path 必需。规定要读取的文件
include_path 可选。如果您还想在 include_path(在 php.ini 中)中搜索文件的话,请设置该参数为 '1'
context 可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 NULL,则忽略
start 可选。规定在文件中开始读取的位置。该参数是 PHP 5.1 中新增的
max_length 可选。规定读取的字节数。该参数是 PHP 5.1 中新增的

Python3 chr()函数

chr(i)

用一个整数作参数,返回值是当前整数对应的 ASCII 字符

参数 描述
i 可以是 10 进制也可以是 16 进制的形式的数字,数字范围为 0 到 1,114,111 (16 进制为0x10FFFF)

JavaScript encodeURIComponent() 函数

encodeURIComponent(uri)

可把字符串作为 URI 组件进行编码

该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。

其他字符(比如 :; / ? : @ & = + $ , # 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的

参数 描述
uri 必需。一个字符串,含有 URI 组件或其他要编码的文本

jQuery val() 方法

返回 value 属性:
$(selector).val()

设置 value 属性:
$(selector).val(value)

通过函数设置 value 属性:
$(selector).val(function(index,currentvalue))
参数 描述
value 必需。规定 value 属性的值
function(index,currentvalue) 可选。规定返回要设置的值的函数
index——返回集合中元素的 index 位置
currentvalue——返回被选元素的当前 value

jQuery ajax() 方法

$.ajax({name:value, name:value, ... })

ajax()方法通过 HTTP 请求加载远程数据。

该方法是 jQuery 底层 AJAX 实现。

$.ajax() 返回其创建的 XMLHttpRequest 对象。

大多数情况下无需直接操作该函数,除非需要操作不常用的选项,以获得更多的灵活性。

最简单的情况下,$.ajax() 可以不带任何参数直接使用。

参数 描述
async
beforeSend(xhr) 发送请求前运行的函数
cache
complete(xhr,status) 请求完成时运行的函数(在请求成功或失败之后均调用,即在 success 和 error 函数之后)
contentType 发送数据到服务器时所使用的内容类型。默认是:"application/x-www-form-urlencoded"
context 为所有 AJAX 相关的回调函数规定 "this" 值
data 规定要发送到服务器的数据
dataFilter(data,type) 用于处理 XMLHttpRequest 原始响应数据的函数
dataType 预期的服务器响应的数据类型
error(xhr,status,error) 如果请求失败要运行的函数
global 布尔值,规定是否为请求触发全局 AJAX 事件处理程序。默认是 true
ifModified 布尔值,规定是否仅在最后一次请求以来响应发生改变时才请求成功。默认是 false
jsonp 在一个 jsonp 中重写回调函数的字符串
jsonpCallback 在一个 jsonp 中规定回调函数的名称
password 规定在 HTTP 访问认证请求中使用的密码
processData 布尔值,规定通过请求发送的数据是否转换为查询字符串。默认是 true
scriptCharset 规定请求的字符集
success(result,status,xhr) 当请求成功时运行的函数
timeout 设置本地的请求超时时间(以毫秒计)
traditional 布尔值,规定是否使用参数序列化的传统样式
type 规定请求的类型(GET 或 POST)
url 规定发送请求的 URL。默认是当前页面
username 规定在 HTTP 访问认证请求中使用的用户名
xhr 用于创建 XMLHttpRequest 对象的函数

正则表达式

"/" 是表达式开始和结束的标记

\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束

\W 匹配任意字母,数字,下划线,汉字的字符
\S 匹配任意空白符的字符
\D 匹配任意数字的字符
\B 匹配单词开头或结束的位置


[极客大挑战 2019]Http

遇到http类题目优先考虑抓包

有个没被访问的Secret.php文件,访问一下

需要加个Referer

需要改一下User-Agent

要本地访问,加个X-Forwarded-For

扩展

HTTP Referer

是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的

HTTP X-Forwarded-For

通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段

X-Forwarded-For: client, proxy1, proxy2

XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP,用户真实 IP 为 IP0


[极客大挑战 2019]PHP

这题完全没头绪,直接学的wp

用dirsearch扫描网站目录(扫描时选择php类型)

漫长的扫描后,唯有一个反馈是200的(其余的不是429就是503),那么这个应该就是要找的东西

输入url之后直接下载下来了www.zip

flag.php中给出的东西输入发现不对

index.php里面有这样一段

先调用了class.php

用get方式接受输入,然后赋给select

把select反序列化后赋给res

再去看看class.php

核心代码是这一段,需要满足username是admin,password是100,在最后执行__destruct函数时能拿到flag

接下来构造序列化

得到序列化的结果

其实还可以在输出的时候进行url编码,防止%00对应的不可打印字符在复制时丢失

var_dump(urlencode(serialize($a)));

我在vscode集成控制台,直接右键用PHP Server的Serve project和直接网页形式直接打开得到是三种不同结果

PHP Server:Serve project得到的是上图,直接忽略了无法打印的字符

vscode控制台输出的是\000(\000也是ASCII的0)

网页直接打开是无法显示的字符

需要注意的是使用时将编码的结果中Name后面的2换成3或其他值(主要是编码后确实不是很方便观看)

在执行__destruct函数之前会执行一次__wakeup函数导致用户名不再是admin,此时就需要绕过

在反序列化字符串时,属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行

将序列化字符串改成这样

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

private字段的序列化,在类名和字段名前面都会加上0的前缀,字符串长度也包括所加前缀的长度(因此显示有14个字符但是看起来Nameusername却是12个字符),因此再改一下字符串

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

扩展

序列化与反序列化

序列化能方便储存传输,减轻服务器压力

将变量和对象转换成字符串的过程就是序列化

序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字

对字母大小写和空白(空格、回车、换行等)敏感,字符串是按照字节计算的

序列化后的字段名中不包括声明时的变量前缀符号$

所有字母对应含义:

a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string

数组(array)通常被序列化为:

a:<n>:{<key 1><value 1><key 2><value 2>...<key n><value n>}

其中 表示数组元素的个数,<key 1>、<key 2>…… 表示数组下标,<value 1>、<value 2>…… 表示与下标相对应的数组元素的值

对象(object)通常被序列化为:

O:<length>:"<class name>":<n>:{<field name 1><field value 1><field name
2><field value 2>...<field name n><field value n>}

其中 表示对象的类名 的字符串长度, 表示对象中的字段1个数。

这些字段包括在对象所在类及其祖先类中用var、public、protected 和private 声明的字段,但是不包括static 和 const 声明的静态字段,也就是说只有实例(instance)字段。

<filed name 1>、<filed name 2>……表示每个字段的字段名,而<filed value 1>、<filed value 2>…… 则表示与字段名所对应的字段值。

字段名是字符串型,序列化后格式与字符串型数据序列化后的格式相同。

字段值可以是任意类型,其序列化后的格式与其所对应的类型序列化后的格式相同。

public、protected与private在序列化时的区别

public 声明的字段是公共字段,在所声明的类和该类的子类中可见,在该类的对象实例可见。

因此公共字段的字段名按照声明时的字段名进行序列化

———————————————————————————————————————————————————————

protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。

因此保护字段的字段名在序列化时,字段名前面会加上%00*%00的前缀。这里的 %00 表示 ASCII 码为 0 的字符(不可见字符)

———————————————————————————————————————————————————————

private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。

因此私有字段的字段名在序列化时,字段名前面会加上 %00%00 的前缀。

这里 表示的是声明该私有字段的类的类名,而不是被序列化的对象的类名,因为声明该私有字段的类不一定是被序列化的对象的类,而有可能是它的祖先类。

字段名被作为字符串序列化时,字符串值中包括根据其可见性所加的前缀。

字符串长度也包括所加前缀的长度,其中%00字符也是计算长度的

ASCII码 0值

ASCII码第一个值就是0,十六进制是0x00,也就是null(空字符)

\0是字符,值等效于ASCII的0;PHP中变量是以C语言的结构体来存储的,空字符串和NULL,false,以及"\0"都是以值为0存储的

疑问

%00到底是什么,为什么查表得到url对应值是æ但实际上又应该等于0?

描述:

但实际上æ的ASCII是%C3%A6,并不是%00,而从上面的题目来看%00确实不能打印出来,但是应该对等于0

答:

URL编码遵循下列规则:

每对name/value由&;符分开;每对来自表单的name/value由=符分开。

如果用户没有输入值给这个name,那么这个name还是出现,只是无值。

任何特殊的字符(就是那些不是简单的七位ASCII,如汉字)将以百分符%用十六进制编码,当然也包括象 =,&;,和 % 这些特殊的字符

其实url编码就是一个字符ascii码的十六进制,不过稍微有些变动,需要在前面加上“%”。比如“\”,它的ascii码是92,92的十六进制是5c,所以“\”的url编码就是%5c。

先来看ASCII码的前八位

再看URL编码的前八位

由此是能得出url编码与ASCII编码的对应关系,那么前七位URL编码的值确实就是没有办法打印出来的,而æ应该算是乱码之类的东西

也就能知道,%00确实是ASCII的0,但是不能打印出来

魔术方法

PHP将所有以 __ (两个下划线)开头的类方法保留为魔术方法

PHP __sleep() 方法

在使用 serialize() 函数时,程序会检查类中是否存在一个 __sleep() 魔术方法

如果存在,则该方法会先被调用,然后再执行序列化操作

该方法可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组

PHP __wakeup() 方法

使用 unserialize() 函数会检查是否存在一个 __wakeup() 方法

如果存在,则会先调用该方法,预先准备对象需要的资源

该方法可以重新建立数据库连接,或执行其它初始化操作

_wakeup()函数漏洞原理:反序列化字符串时,属性个数的值大于实际属性个数时,会跳过 __wakeup()函数的执行

PHP __toString() 方法

用于一个类被当成字符串时应怎样回应

PHP __invoke() 方法

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用

PHP __construct() 方法

php中构造方法是对象创建完成后第一个被对象自动调用的方法

在每个类中都有一个构造方法,如果没有显示地声明它,那么类中都会默认存在一个没有参数且内容为空的构造方法

通常构造方法被用来执行一些有用的初始化任务,如对成员属性在创建对象时赋予初始值

在同一个类中只能声明一个构造方法,原因是PHP不支持构造函数重载

PHP __destruct() 方法

与构造方法对应的就是析构方法

析构方法允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件、释放结果集等

析构函数不能带有任何参数

PHP __set() 方法

该方法用来设置私有属性, 给一个未定义的属性赋值时,此方法会被触发,传递的参数是被设置的属性名和值

PHP __get() 方法

在php中,类的成员属性被设定为 private 后,试图在外面调用它则会出现“不能访问某个私有属性”的错误

使用该方法可以在对象的外部获取私有成员属性的值

PHP __isset() 方法

当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用

对比一下 isset()函数的应用:isset()是测定变量是否设定用的函数,传入一个变量作为参数,如果传入的变量存在则传回true,否则传回false。

如果在一个对象外面使用isset()这个函数去测定对象里面的成员是否被设定可不可以用它呢?

分两种情况,如果对象里面成员是公有的,我们就可以使用这个函数来测定成员属性;如果是私有的成员属性,这个函数就不起作用了,原因就是因为私有的被封装了,在外部不可见。

如果在类里面加上一个__isset()方法,当在类外部使用isset()函数来测定对象里面的私有成员是否被设定时,就会自动调用类里面的__isset()方法完成这样的操作

PHP __unset() 方法

当对不可访问属性调用unset()时被调用

对比一下 unset() 函数的应用:unset()这个函数的作用是删除指定的变量且传回true,参数为要删除的变量

那么如果在一个对象外部去删除对象内部的成员属性用unset()函数可以吗?

分两种情况,如果一个对象里面的成员属性是公有的,就可以使用这个函数在对象外面删除对象的公有属性;如果对象的成员属性是私有的,我使用这个函数就没有权限去删除

如果你在一个对象里面加上__unset()这个方法,就可以在对象的外部去删除对象的私有成员属性了

在对象里面加上了__unset()这个方法之后,在对象外部使用“unset()”函数删除对象内部的私有成员属性时,对象会自动调用__unset()函数删除对象内部的私有成员属性。

PHP __call() 方法

为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免

该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去

PHP __callStatic() 方法

用静态方式中调用一个不可访问方法时调用

与__call() 方法一致,区别就是 __callStatic()是为静态方式准备的

PHP __set_state() 方法

调用var_export()导出类时,此静态方法会被调用

本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性

PHP __clone() 方法

对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用

PHP __autoload() 方法

通过定义这个函数来启用类的自动加载

PHP __debugInfo()方法

打印所需调试信息

$this ->

$this -> 的含义是表示 实例化后的 具体对象

我们一般是先声明一个类,然后用这个类去实例化对象

比如这样:

class User
{
    public $name;
      function GetName() 
      {
           echo $this->name;
      }
}

使用的时候

$user = new User();

$user -> name = '冰枫';

$user -> GetName();  //这里就会输出 冰枫

原理是调用 $user -> GetName()的时候,上面User类中的代码 echo $this -> name ,就是相当于是 echo $user -> name;


[极客大挑战 2019]Upload

是个上传页面,考虑文件上传漏洞(我传真正的图片居然告诉我 Not image!)

写个一句话木马,上传后抓包,改一下文件类型

发现被拦截了,原因是&#x3C,&#x3F

这两个字符是十六进制,转换过来是 < 和 ?,也就是说最开始的 <? 被过滤了

突然发现burpsuite有个Render能直接渲染页面(压根不需要去特地找转义序列)

不能使用php的一句话木马,那就用javascrpit的

这是检查了头文件,加上GIF89a绕过

毕竟需要让后台用php解释器解释一句话木马,改一下后缀名

发现php被过滤了

试试看别的php扩展名,试出来phtml没有过滤

接下来就是蚁剑连接

一开始是直接复制粘贴的url,发现连接失败,说明需要直接连接自己传入的文件才行

但是路径我不知道啊,于是就尝试uplaod有关的组合,最终发现路径就是upload

优先去根目录寻找flag

疑问

为什么加上GIF89a就能绕过文件头检测呢?

答:

现在几乎所有的gif图片的文件头都是GIF89a,也就是说如果头文件检测到GIF89a,那么不太严格的过滤就会认为这个是gif图片文件

至于为什么头文件是gif,后缀名却可以是别的,可以理解为头文件检测和后缀名检测是分开的,如果严格过滤那么就没法通过了

为什么用的是gif的头文件而不是别的图片的头文件呢?

答:

常见的图片格式也就是jpg,png和gif

jpg图片格式由 SOI(文件头)、APP0(图像识别信息)、DQT(定义量化表)、SOF0(图像基本信息)、DHT(定义Huffman表) 、DRI(定义重新开始间隔)、SOS(扫描行开始) 和 EOI(文件尾)组成

jpg图片的文件头字符组成是:ÿØ

png图片结构由文件署名和数据块(chunk)组成

png图片的文件署名域用来识别该文件是不是png文件,字符组成是:‰PNG (乱码了)

gif图片结构由 文件头(File Header)、GIF数据流(GIF Data Stream) 和 文件终结器(Trailer) 三个部分组成

gif图片的文件头由 GIF署名(Signature) 和 版本号(Version) 组成

GIF署名用来确认一个文件是否是gif格式的文件,这一部分由三个字符组成:"GIF"

文件版本号也是由三个字节组成,可以为"87a"或"89a"

所以最终看到gif的文件头是:GIF89a 或者 GIF87a

因此我认为从输入头文件的便捷性方面考虑,用gif的头文件去绕过更加方便

扩展

HTML、XML 等 SGML 类语言的转义序列

形如

&#dddd;
&#xhhhh;
&name;

它们不是编码,是转义序列(escape sequence)

以 HTML 举例,这三种转义序列都称作 character reference:

  • 前两种是 numeric character reference(NCR),数字取值为目标字符的 Unicode code point;
    以「&#」开头的后接十进制数字,以「&#x」开头的后接十六进制数字。

  • 后一种是 character entity reference,后接预先定义的 entity 名称,而 entity 声明了自身指代的字符。

PHP的扩展名

PHP的扩展名除了本身的php之外,还有:

php3,php4,php5,phtm,phtml,phps

  • php3,php4,php5 等效于php,是网页

  • phtm,phtml 含有php代码的html文本(htm与html本质上是一样的扩展名,由于DOS时代的8.3文件名命名原则,所以出现了htm的写法,类似的还有jpeg与jpg)

  • phps 是php源代码

想让服务器能接受这些扩展名,需要在Apache的配置文件httpd.conf中加入这些后缀名

AddType application/x-httpd-php .php .phtml .php3 .php4 .php5

GIF89a文件头

GIF89a图形文件是一个根据图形交换格式(GIF)89a版(1989年7 月发行)进行格式化之后的图形。

在GIF89a之前还有87a版(1987年5月发行),但在Web上所见到的大多数图形都是以89a版的格式创建的。

在过滤头文件时,常用于头文件欺骗,让服务器认为你传入的是图片


posted @ 2021-03-29 23:42  Arctic_Maple  阅读(292)  评论(0编辑  收藏  举报