How I hacked an ESA's experimental satellite(译文)

0x00 背景

免责声明:早在两个月之前,我们就已经将文中介绍的漏洞提交给了ESA;目前,这些漏洞已经得到了修复。同时,本文发表前已经通过了ESA的评审,并允许公之于众。

Hacking satellite

这两个字,听起来真的令人印象深刻。至少对我来说是这样。

这是黑客文化中的一个梦想,经常出现在关于间谍/黑客题材的电视剧/电影中,藉此可以“重定向卫星”,以便从轨道上更好地观察敌人的行动。

从小时候起,我就一直梦想着获得这样的技能:黑掉一些你看不到它们,但它们却能看到你的东西。由于卫星能够赋予用户强大的力量,所以,它自然是国家防御中最关键的系统之一,因此,卫星应该很难被黑客侵入的。

0x01 Or are they ?

今年一月,我无意中发现了一则黑客大赛的广告,该赛事是由一家名为CYSEC的企业组织的,该企业专门从事航天工业。

该企业呼吁黑客积极参与,其想法是在名为HackCysat的活动中现场演示对卫星等关键设备进行攻击的可能性和风险。

这个想法是为了提高航天工业对这些越来越普遍的风险的认识,尤其针对新空间的安全威胁。

这次演示的攻击目标是ESA的下一代实验卫星,称为OPS-SAT,由同名研究团队建造并由David Evans直接领导。

1.png

规则很简单,攻击只允许利用有效载荷,官方提供系统映像,同时,ESA的实验员还能通过实际的OPS-SAT来演示和检验相关的攻击方法。

众所周知,入侵卫星可不是简单的事情,为此,让我们先从阅读研究团队的出版物开始,以大致了解卫星的大小,以及研究和入侵此类设备需要具备哪些能力。

于是,我注册了“OPS-SAT experimenter”,完成确认后,我们就可以着手进行研究了。

1.png

0x02 Architecture overview

OPS-SAT是一颗非常酷的立方体卫星,用于展示围绕“NanoSAT-MF”的嵌入式实验架构。

这种架构的设计初衷是:通过允许从地球向卫星远程上传应用程序,为当前的卫星结构提供更多的灵活性。

也就是说,允许地面操作人员对卫星进行升级,或进行新的测试,以提供更大的灵活性和更长的使用寿命。

0x03 Baremetal

为了克服代表这种项目的技术挑战,OPS-SAT团队提出了一个相当有趣的架构,可以重新使用商业嵌入式计算领域的成熟技术,并使其适应太空使用。该架构分为两部分:Bus(总线)和Payload(有效载荷)。

总线通过其超高频天线/收发器进行(低级别)通信。同时,它还可以作为备份解决方案来重置系统,并在CCSDS引擎无法正常工作时传输数据。这是一个非常强大的解决方案。

有效载荷由4个MitySOM-5CSX SoM组成,它们位于同一主板上,以冷冗余方式运行,同时,还有4张1GB的microSD卡。这一部分被称为SEPP,即“Satellite Experimenters Processing Platform”的缩写。借助于它,就能够实现与不同的传感器(如高清摄像头)进行高级通信。其中,所有部件都是通过i2c进行通信的,而i2c则是作为应用层传输CAN协议来使用的。

1.png

0x04 Sweet SEPP

1.png

这款MitySOM-5CSX SoM配备了一个双核Cortex-A9@800MHz处理器,以及1GB容量的RAM。这个SoM(System On Module)可以运行Yocto Linux系统,这是一个基于busybox的Linux发行版。

对于每位注册的参赛者,OPS-SAT小组都会为其提供卫星当前使用的系统的磁盘映像。

0x05 Software

我做的第一件事就是深挖研究论文,因为它们为理解攻击目标、面临问题、解决方案、以及已经做出的选择所需要的一切。

对于NanoSAT MF架构的第一个目标,就是获取上传/测试新实验或程序的权限。而这些应用程序都处于NanoSAT MF Supervisor的控制之下。简单来说,它就是所有这些软件架构的核心。

该系统采用java语言开发,实现了软件包管理、应用程序管理、传感器控制等高级通信功能。此外,为了便于通信,它还提供了一个易于使用的IPC-CAN API。根据实现的不同,既可以直接授予其root权限,也可以通过使用sudo(我们稍后会讨论)来获得root权限。

为了与这个supervisor以及它所控制的传感器进行交互,应用程序需要借助于NanoSAT MF框架,该框架能够将Java调用转换为IPC-CAN协议。

另外,这个框架是开源的,所有人都可以查看其源代码。

同时,许多示例和实际应用程序也可以从开源代码中获得,如“OPS-SAT SmartCam”:

https://github.com/georgeslabreche/opssat-smartcam

0x06 Ground station & application upload process

虽然地面站和通信总线系统超出了本文的讨论范围,但我认为,简要回顾这两个概念有助于更好地理解OPS-SAT团队的工作。

1.png

为了上传和控制卫星上的应用程序,OPS-SAT团队开发了一个“数据中继”服务器,可以用经典的以太网IP地址、TCP/UDP协议进行调用,该服务器将把实验者的数据发送到“任务控制系统”,并将这些数据转换成空间协议。

为了上传数据,“数据中继”服务器支持SFTP上传以及MAL协议。

为了演示我们的攻击过程,OPS-SAT团队允许我们以普通实验者的身份访问这个“数据中继”。

0x07 Let's hack

经过上述简单介绍之后不难看出,入侵的重点在于Supervisor:只要拿下它,就能全面接管卫星;另一个切入点,就是上面介绍过的的框架。

Extraction of the Supervisor JAR file

ESA提供了一个系统映像和一个bmap文件——简单来说,就是分区映射。

<bmap version="2.0">
    <!-- Image size in bytes: 1.3 GiB -->
    <ImageSize> 1400897536 </ImageSize>

    <!-- Size of a block in bytes -->
    <BlockSize> 4096 </BlockSize>

    <!-- Count of blocks in the image file -->
    <BlocksCount> 342016 </BlocksCount>

    <!-- Count of mapped blocks: 235.9 MiB or 17.7%  -->
    <MappedBlocksCount> 60396  </MappedBlocksCount>

    <!-- Type of checksum used in this file -->
    <ChecksumType> sha256 </ChecksumType>

    <!-- The checksum of this bmap file. When it is calculated, the value of
         the checksum has be zero (all ASCII "0" symbols).  -->
    <BmapFileChecksum> 212a4feebbaab127e7593ed5288f9389fa4396facb2a432afda0d47cbd56c1d1 </BmapFileChecksum>

    <!-- The block map which consists of elements which may either be a
         range of blocks or a single block. The 'chksum' attribute
         (if present) is the checksum of this blocks range. -->
    <BlockMap>
        <Range chksum="e5432c80576c2563b429691c78c7876be9fde919d8b23780bcc277154473d6d2"> 0-2 </Range>
        <Range chksum="c0255c154275a87fed18de9b06fda506570fea536ad0e2e52f4cf9351488ebdf"> 256-435 </Range>
        <Range chksum="8c47b45ad9528f79c10fd1619e7506e7d0b33c53ec51cb6a0d7406abed361b4c"> 1024-2297 </Range>
        <Range chksum="20e87e063b4a577f0f8cef3047e35204e7b31ffd63875cde6338749f5f7c2210"> 2299 </Range>
        <Range chksum="38f9ab617db643ae3459518a47e48cede384a39dd1e35ba3c3660f21eb0709ad"> 79872-79939 </Range>
        <Range chksum="349968fd865141e3c79074834d8b03bb23aad9829a13efd25c1692b0a20d8c75"> 79941 </Range>
        <Range chksum="78ccaf938af5401a2271363e026fa0fa48bf82d6bca1164e34b2b8a18ef638bf"> 79944-79945 </Range>
        <Range chksum="f1b5250fbbbb13f6c76976a33891860c7353c7c82495c0c295f88440ec6794b1"> 79953-80296 </Range>
        <Range chksum="8b10e215f0b0a2d0e0b68b43ecb4f0d5ffb49d42906e133b7171bbd11b69cebd"> 88145-112641 </Range>
        <Range chksum="6a1d711afa954c43cc8e77ae664cf4aaefc5390df387a5fc4f23f30638de7a0a"> 112705-146720 </Range>
        <Range chksum="3907e1c2a3bfc251657a4d3d72b45376db367f3ee6db3d3f1e01257ce7ca22a8"> 178176-178177 </Range>
        <Range chksum="cab9133f6383f52dc7daa567013796faa56e1b1231208ae9814d3faa63b72cd8"> 210944 </Range>
        <Range chksum="6bf57f50a15d01b7a07ceba835b262d94e84a83c50ace5d8c4b329220456ab17"> 243712-243713 </Range>
        <Range chksum="c90ceb4ec39ad7c1830094cd222e9520eba1ef0a0ff095a25d743a35e508fb3b"> 309248-309249 </Range>
        <Range chksum="f3cc103136423a57975750907ebc1d367e2985ac6338976d4d5a439f50323f4a"> 342013-342015 </Range>
    </BlockMap>
</bmap>

由于supervisor可能使用了extX文件系统,因此,我决定直接用binwalk下手:

binwalk -e ops-sat-image.mityarm-201810171116-mmcblk.direct

然后,在ext-root中可以看到:

1.png

接下来,用grep搜索所有.jar文件:

1.png

仔细看看下面这行:

lib/systemd/system/nmf.service:ExecStart=/usr/bin/java -classpath "/usr/lib/java/*" esa.mo.nmf.provider.NanoSatMOSupervisorOPSSATImpl

据我们所知,classpath的值是/usr/lib/java,因此,其内容如下所示:

1.png

Reverse Engineering of the Supervisor

当我面对Java应用程序或.jar文件时,我通常会直接使用JADX或任何其他Java字节码反编译器,具体取决于它们给出的结果。

再一次,结果没有让我们失望:ESA没有从生产映像中删除符号或函数/变量名。这样的话,逆向分析就容易多了。我认为这是一个缺陷,我们稍后再谈。

Studying of the code

反编译后我做的第一件事,就是在每个文件中搜索一些关键字。同时,还测试了各种常见的漏洞,如RCE(远程代码执行)。

以下是搜索关键字shell时得到的一些结果:

1.png

我们对最后一个非常感兴趣,下面是它的代码:

String str = (new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS")).format(new Date(System.currentTimeMillis() + delta));
ShellCommander shell = new ShellCommander();
shell.runCommand("date -s \"" + str + " UTC\" | hwclock --systohc");

这正是我要找的切入点。这个调用包含一个字符串连接,可能作为RCE的注入点。但实际上并非如此,因为str是一个格式太多且没有留给用户任何控制的字符串。

在确定了几个切入点之后,我就开始阅读它们周围的所有代码,并试图了解如何操纵输入,以及如何通过用户操作来创造符合要求的情况。

当然,这只是一个开始,如果你想真正了解你的目标,你需要阅读所有的东西,提取代码,进行测试,等等。

0x08 Vulnerabilities found

下面介绍的所有漏洞都已经报告给了ESA,并且我亲自审查了这些漏洞的修复代码。

Privileged Path traversal write by Zip Slip on upload MAL action. (critical)

对代码进行一番研究之后,我发现了一个名为NMFPackageManager的类,它负责应用程序的上传、启动等工作。需要指出的是,所有这些类的方法都是用于跟系统或文件系统打交道的,并且可以通过MAL访问权限直接调用。

这里我们感兴趣的是installFiles方法,一旦软件包被上传,它就负责解压缩,并将文件移动到预定的路径。

            private static String generateFilePathForSystem(String path) {
/* 351 */     String out = path.replace('/', File.separatorChar);
/* 352 */     String out2 = out.replace('\\', File.separatorChar);
/* 353 */     return out2;
/*     */   }

            private static void installFiles(NMFPackageDescriptor descriptor, ZipFile zipFile) throws IOException {
/* 359 */     File installationFolder = getInstallationFolder();
/* 362 */     byte[] buffer = new byte[1024];
/* 365 */     for (int i = 0; i < descriptor.getFiles().size(); i++) {
/* 366 */       NMFPackageFile file = descriptor.getFiles().get(i);
/* 367 */       ZipEntry entry = zipFile.getEntry(file.getPath());
/* 369 */       String path = generateFilePathForSystem(entry.getName());
/* 370 */       File newFile = new File(installationFolder.getCanonicalPath() + File.separator + path);
/* 371 */       (new File(newFile.getParent())).mkdirs();
/* 373 */       Logger.getLogger(NMFPackageManager.class.getName()).log(Level.INFO, "Copying the file to location: " + newFile
/* 374 */           .getCanonicalPath());
/* 376 */       FileOutputStream fos = new FileOutputStream(newFile);
/* 379 */       InputStream zis = zipFile.getInputStream(entry);
/*     */       int len;
/* 381 */       while ((len = zis.read(buffer)) > 0)
/* 382 */         fos.write(buffer, 0, len);
/* 385 */       fos.close();
/* 387 */       long crc = HelperNMFPackage.calculateCRCFromFile(newFile.getCanonicalPath());
/* 391 */       if (file.getCRC() != crc)
/* 392 */         throw new IOException("The CRC does not match!");
/*     */     }
/*     */   }

这段代码中易受攻击部分是下面这三行:

/* 367 */       ZipEntry entry = zipFile.getEntry(file.getPath());
/* 369 */       String path = generateFilePathForSystem(entry.getName());
/* 370 */       File newFile = new File(installationFolder.getCanonicalPath() + File.separator + path);

简单来说,这段代码首先获取当前的zipFile条目,然后从entry.name中获得path(这是相对于根归档的相对路径),然后将它们合并,以便在它们之间只使用一个file.separator

实际上,上面的代码容易受到ZipSlip漏洞的攻击。

在这些解压缩功能的实现代码中,ZipSlip是一种非常常见的安全漏洞。

这些漏洞的根源在于对zip格式的过度信任。

因为zip格式允许归档内容文件包含任何路径有效字符作为文件名,其中包括../,而它们又可以用来描述文件在上一个目录路径中的位置,例如../../myfile.txt

当实现代码只是将条目名称与目标目录合并时,就容易受到路径遍历写入漏洞的影响,即允许将任何文件写入文件系统的任何位置。

在目标目录为/home/exp1013/packages/的情况下,如果合并条目名为../../../root/hello.txt,将导致以下结果:

/home/exp1013/packages/../../../root/hello.txt

这等价于:

/root/hello.txt

结合下一个漏洞,并以特定文件为目标,就能够以root权限完全接管卫星。

Run tests & Exploitation

在进行复杂的逆向分析的时候,最好使用QEMU直接模拟系统。但是就这里来说,由于使用了MAL协议,这使用使事情变得更加复杂,同时,我的研究时间也非常有限。

因此,在验证函数输入上没有发生任何可能使我的测试失灵的修改之后,我决定只提取这部分代码并在本地进行测试。

要想被supervisor所接受,存档需要符合NMF Package的格式。这种格式使用一个名为nmfpackage.receive的文件作为文件索引和包描述符。

经过一番逆向分析之后,我终于弄清楚了它的格式,下面是一个例子:

NMFPackageDescriptorVersion=1
PackageName=hello
PackageVersion=0.1
PackageCreationTimestamp=1643657215
FilePath=myfile.txt
FileCRC=3795452057
FilePath=myfile2.txt
FileCRC=3795452057
...

它是由以下部分组成:

包格式的版本
包的名称
包的版本
创建软件包的时间戳

然后,对于压缩包中的每个文件,都有一个对应的FilePath和FileCRC条目链。其中,FilePath是压缩包条目的相对路径,而FileCRC则是文件相应的CRC-32哈希值(十进制),这个哈希值必须与supervisor计算的哈希值一致,才能成为一个有效的包。

在我们的例子中,我们需要在路径中添加包含回车符的文件,因为它对应于我们的条目名称以及正确的CRC,然后添加nmfPackage.receipt和我们的条目,以获取exploit。

不幸的是,大多数存档管理器都不允许在条目名称中包含回车符。

为此,我尝试使用evilarc项目(https://github.com/ptoomey3/evilarc),但由于缺乏维护人员,很难弄清楚其正确用法。同时,它也无法全面满足我的需求。所以,我决定通过python来使用zipfile库。

这就是我们的软件包!

为了测试它,我只需要放一个回车符和虚拟有效载荷。然后,将我的tester指向它,验证该漏洞的可利用性。

这显然是非常关键的,因为它可以在不需要启动应用程序的情况下触发。

IRL攻击的一个简单场景是通过冒充一个普通的实验者来诱骗OPS-SAT团队上传这个软件包。

还没等到比赛结束,我就向OPS-SAT团队展示这个漏洞。

0x09 Mitigation

    不要信任用户的输入。 
File newFile = new File(installationFolder.getCanonicalPath() + File.separator + path);

上面一行代码,就是前面描述的易受攻击的实现中的漏洞所在位置。一个快速的修复方法,就是修补generateFilePathForSystem函数,以对相应字符进行消毒处理,就像这样:

            private static String generateFilePathForSystem(String path) {
              String out = path.replace('..', ''); // Just in case bad people do bad things
/* 351 */     String out1 = out.replace('/', File.separatorChar);
/* 352 */     String out2 = out1.replace('\\', File.separatorChar);
/* 353 */     return out2;
/*     */   }

但是更好的方法是使用像dirname这样的函数(Java不是我的主要语言),来获得路径末端的文件名。

Sudo misconfiguration & Supervisor run as root. (medium)

此外,当我研究一个新系统时,我总是检查一下/etc/passwd与/etc/sudoers文件。

root:x:0:0:root:/home/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
systemd-bus-proxy:x:991:988::/:/bin/nologin
polkitd:x:992:989::/etc/polkit-1:/bin/sh
systemd-resolve:x:993:990::/:/bin/nologin
systemd-network:x:994:991::/:/bin/nologin
systemd-timesync:x:995:992::/:/bin/nologin
systemd-journal-upload:x:996:993::/:/bin/nologin
messagebus:x:997:996::/var/lib/dbus:/bin/false
sshd:x:998:998::/var/run/sshd:/bin/false
avahi:x:999:999::/var/run/avahi-daemon:/bin/false
supervisor:x:1000:1000::/home/supervisor:/bin/bash
exp-1024:x:1024:1001::/home/exp-1024:/bin/bash
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh

/etc/sudoers被配置为导入/etc/sudoers.d中的所有配置文件,其中有一个名为supervisor的文件。

supervisor ALL = (ALL) NOPASSWD

这里,supervisor用户的sudo被配置为授予root权限,而不需要提供任何密码。

显然,这是一个非常糟糕的主意,因为在RCE(远程代码/命令执行)的情况下,提权易如反掌。

ESA团队后来向我证实,在生产环境中,他们刚刚以超级用户身份启动了Supervisor。

所以在生产环境中,如果攻击者实现了RCE……他们就立即变身为root用户。再说一遍,不要这样做。

Remote command execution on app starting (minor)

另一个很有可能被利用的漏洞是startApp函数的实现,它可以通过MAL操作直接调用。

/*     */   protected String[] assembleAppLauncherCommand(String appName, String directoryServiceURI) {
/* 360 */     ArrayList<String> ret = new ArrayList<String>();
/* 361 */     String trimmedAppName = appName.replaceAll("space-app-", "");
/* 362 */     if (this.osValidator.isWindows()) {
/* 363 */       ret.add("cmd");
/* 364 */       ret.add("/c");
/* 365 */       ret.add("set JAVA_OPTS=-Desa.mo.nmf.centralDirectoryURI=\"" + directoryServiceURI + "\" && " + trimmedAppName + ".bat");
/*     */     } else {
/* 368 */       ret.add("/bin/sh");
/* 369 */       ret.add(trimmedAppName + ".sh");
/*     */     } 
/* 371 */     return ret.<String>toArray(new String[ret.size()]);
/*     */   }
/*     */   
/*     */   protected String[] assembleAppLauncherEnvironment(String directoryServiceURI) {
/* 375 */     if (!this.osValidator.isWindows()) {
/*     */       String[] currentEnv;
/*     */       try {
/* 379 */         currentEnv = EnvironmentUtils.toStrings(EnvironmentUtils.getProcEnvironment());
/* 380 */       } catch (IOException ex) {
/* 382 */         currentEnv = new String[0];
/*     */       } 
/* 384 */       ArrayList<String> ret = new ArrayList<String>(Arrays.asList(currentEnv));
/* 385 */       ret.add("JAVA_OPTS=-Desa.mo.nmf.centralDirectoryURI=" + directoryServiceURI + "");
/* 386 */       return ret.<String>toArray(new String[ret.size()]);
/*     */     } 
/* 388 */     return null;
/*     */   }
/*     */   
/*     */   protected void startAppProcess(AppsLauncherProviderServiceImpl.ProcessExecutionHandler handler, MALInteraction interaction, String directoryServiceURI) throws IOException {
/* 395 */     AppDetails app = (AppDetails)getDef(handler.getAppInstId());
/* 399 */     File appFolder = new File(this.appsFolderPath + File.separator + app.getName().getValue());
/* 400 */     String[] appLauncherCommand = assembleAppLauncherCommand(app.getName().getValue(), directoryServiceURI);
/* 402 */     String[] appLauncherEnvironment = assembleAppLauncherEnvironment(directoryServiceURI);
/* 403 */     Logger.getLogger(AppsLauncherManager.class.getName()).log(Level.INFO, "Reading and initializing ''{0}'' app in dir: {1}, using launcher command: {2}, and env: {3}", new Object[] { app
/*     */           
/* 405 */           .getName().getValue(), appFolder.getAbsolutePath(), Arrays.toString((Object[])appLauncherCommand), 
/* 406 */           Arrays.toString((Object[])appLauncherEnvironment) });
/* 408 */     final Process proc = Runtime.getRuntime().exec(appLauncherCommand, appLauncherEnvironment, appFolder);
/* 410 */     handler.startPublishing(proc);
/* 412 */     if (proc != null) {
/* 413 */       Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
/*     */               public void run() {
/* 416 */                 proc.destroy();
/*     */               }
/*     */             }));
/* 419 */       this.handlers.put(handler.getAppInstId(), handler);
/* 420 */       setRunning(handler.getAppInstId(), true, interaction);
/*     */     } else {
/* 422 */       Logger.getLogger(AppsLauncherManager.class.getName()).log(Level.WARNING, "The process is null! Something is wrong...");
/*     */     } 
/*     */   }

使用startAppProcess方法启动应用程序时,它会使用提供给它的应用程序名称调用方法assembleAppLauncherCommand,来生成appLauncherCommand命令。之后,这个 appLauncherCommand将直接作为系统命令执行。

为了正确生成这个命令,assembleAppLauncherCommand将检测操作系统(据我所知,这并不意味着它能够在linux之外的操作系统上运行,我觉得这里的检测是出于测试目的),然后,它将根据操作系统生成相应的命令。

对于linux系统,该命令是由上面代码中的第9行和第10行生成。

ArrayList<String> ret = new ArrayList<String>();
...
ret.add("/bin/sh");
ret.add(trimmedAppName + ".sh");
...
return ret.<String>toArray(new String[ret.size()]);

同样,在没有对输入进行任何消毒的情况下,直接将命令与trimmedAppName变量合并,是一个坏主意。

    但是,这样真的容易受到攻击吗?

的确如此,因为trimmedAppName依赖于根应用程序文件夹的名称。

问题在于,因为如果它依赖于一个文件夹的名称,那么它只在Linux上容易受到攻击。

为了在命令字符串中注入我们的有效载荷,我们需要使用特殊字符来连接命令,如&&、|等。

Windows不允许在文件名中使用这些字符,但Linux允许,事实上,我们可以创建一个根文件夹名称为myapp && rm -rf /的应用程序,它一旦启动,就会删除文件系统中的所有内容,而且还是以root权限进行的。

这太可怕了,所以,这的确是一个安全漏洞,但危害不是很大。由于应用程序启动过程实际上就是执行/bin/sh脚本,所以,我们可以创建一个恶意的.sh脚本来利用这个漏洞。总之,代码执行可以通过精心设计来实现的,我们稍后会讲到。

Denial of service on app starting by JVM misconfiguration (medium)

由于JVM的默认配置允许每个程序使用2G的RAM,因此,恶意应用程序可能会使用大量CPU和RAM资源,通过触发swap write来溢出内存并降低整个卫星系统的速度。

只要有足够的时间,就可以触发内核恐慌。

随着反复和持续的攻击,卫星可以在攻击者决定的一段时间内完全无法使用。

这是在JVM之外运行外部应用程序所导致的问题。

最后,为了让他们的实验者控制他们正在测试的应用程序,OPS-SAT团队以无特权用户的身份通过远程shell来控制操作系统。这也是非常糟糕的做法,因为实验者可以尝试通过内核漏洞或CPU漏洞(如Meltdown或Spectre)升级特权。

0x0aGeopolitical implies

卫星已经成为国家冲突的优先目标,随着当前乌克兰和俄罗斯之间持续的冲突,人们再次认识到这一点。

冲突开始几天后,有报道称,黑客攻击欧洲卫星是军事战略的一部分。

这并不奇怪,因为它在过去已经被报道过,而且人们已经想象了很多年了。

正如本文开头所解释的,黑客入侵并因此控制敌方卫星可以切断部分通信、信息收集(如测绘),基本上可以帮助您赢得战争。

ESA确实应该与安全专家合作,以避免这里发现的一些漏洞,其中大多数漏洞都是在几天的时间内找到的,并且使用的技术都非常简单。

正如我在回答“您希望传达什么信息”的问题时所回答的那样:

  • 实现可能存在安全漏洞,大多数实现都是如此。
  • 默认情况下,没有一个系统是安全的。
  • 随着航天工业构建的系统越来越复杂,系统中出现安全漏洞的概率也越来越高。
  • 黑客应该是开发团队的一部分,因为他们知道哪些方面容易出现安全漏洞。

0x0b Report & Collaboration with the ESA

ESA对其实现和架构的批评一直持相当开放的态度。

尤其是在报告的漏洞方面,这一漏洞已经在几周内得到了修复。

我与David Evans团队的关系非常融洽,并且很乐意在未来再次与他们合作。

0x0c CYSEC organisation

当我写完报告后,我立即将其寄给了他们。它基本上就是本文的一个简单摘要。

通过网站提交报告时,遇到了一些问题,最后只好通过邮件寄给他们,随后,他们确认收到了我提交的报告。

然后,我几个星期都没有得到任何消息,直到他们告诉我,我提交的报告不符合要求。

我有点吃惊,因为参赛者并不多,而且我认为我的材料真的很好。

所以,我问:

    为什么?

他们最终承认,我的报告没有被送到评委会。

1.png

他们随后承认活动被取消了……我认为这种做法是对参赛者的不尊重,同时还提醒了我:谨慎参与新出现的赛事。对于新手,我建议坚持参加知名的赛事,以免让人失望。

0x0d Conclusion & thanks

希望你喜欢这篇文章,如果喜欢,请在社交网络上关注我。

如果您认为本人的某个项目对您有用,下面有一个“雇佣我”按钮。

感谢我的每一位读者抽出宝贵的时间阅读本文。

感谢过去几个月给我发邮件的每一个人,非常感谢。

感谢David Evans、Dominik Marszk和ESA的OPS-SAT团队的支持和反馈。

0x0e Sources

https://directory.eoportal.org/web/eoportal/satellite-missions/o/ops-sat
https://www.researchgate.net/publication/303098634_OPS-SAT_Operational_Concept_for_ESA%27S_First_Mission_Dedicated_to_Operational_Technology
https://www.researchgate.net/publication/275639081_CCSDS_Mission_Operations_Services_on_OPS-SAT
https://www.researchgate.net/publication/303098575_OPS-SAT_Preparing_for_the_Operations_of_ESA's_First_NanoSat

原文地址:https://www.deadf00d.com/post/how-to-hack-an-esa-experimental-satellite.html

免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。查看原文

为您推荐