내가 푼 문제의 최근 Writeup
더보기 >
Chatbot
Chatbot 출제된 CTF: Dreamhack CTF Season 2 Round #4 분야: Pwnable 키워드: TCP/IP Server Client, FSB (Format String Bug), GOT Overwrite, Request Bin, File Descriptor Redirection 난이도: ★★☆☆☆ 배경 본 문제는 TCP/IP 소켓 프로그래밍으로 구현된 서버 프로그램이 제공됩니다. 본 문제를 해결하려면, FSB (Format String Bug)로 임의 메모리 주소에 임의 값을 덮어쓸 수 있는 능력이 필요합니다. 공격자는 소켓의 파일 디스크립터로 통신하므로 stdout으로 출력되는 결과를 볼 수 없기 때문에 이 점도 고려해야 합니다. 이를 위해 플래그를 curl 명령어로 Request Bin에 전송하거나, 공격자 소켓의 파일 디스크립터로 리다이렉션할 수 있습니다. 본 문제를 통해 풀이자는 TCP/IP 소켓 프로그래밍으로 구현된 서버 프로그램을 분석하고 취약점을 찾아서 공격하게 됩니다. 이 과정에서 프로그램 분석 능력을 기르고 프로그램 취약점의 위험성을 느끼며, 안전한 소스 코드를 작성하게 되는 데에 취지가 있습니다. 풀이 프로그램 분석 TCP/IP 소켓 프로그래밍으로 구현된 Chatbot 서버와 클라이언트 바이너리가 주어집니다. nc 명령어 또는 클라이언트 바이너리를 사용해서 서버에 접속하여 봇과 채팅을 나눌 수 있습니다. 클라이언트가 서버에 메시지를 보내면 서버는 무작위 메시지를 클라이언트에게 보냅니다. 서버는 몇 가지 명령어를 지원합니다. /quit: 채팅을 끝냅니다. /addmsg [메시지]: 봇이 클라이언트에게 보낼 무작위 메시지에 [메시지]를 추가합니다. FSB (Format String Bug) FSB (Format String Bug)는 printf(), sprintf() 그리고 snprintf()와 같이 포맷 스트링을 사용하는 함수에서 발생하는 버그입니다. "%c %d %x"와 같은 문자열이 포맷 스트링(Format String) 입니다. %c, %d, 그리고 %x는 포맷 식별자(Format Identifier) 입니다. 포맷 식별자가 인자 갯수보다 많으면 FSB라 부릅니다. FSB를 악용하면 공격자는 임의 주소로부터 값을 읽거나 임의 주소에 값을 쓸 수 있습니다. chatbot_server.c에 FSB 취약점이 존재합니다. 클라이언트가 채팅을 보내면 서버는 준비된 무작위 메시지를 클라이언트에게 보냅니다. 그 코드는 다음과 같습니다: c memset(msg, 0x00, sizeof(msg)); botmsg = PickRandomMessage(&botmsgs); snprintf(msg, sizeof(msg), bot_msg, 0); if (write(clnt_sockfd, msg, sizeof(msg)) == -1) PrintError("write() error."); botmsgs에서 클라이언트에게 보낼 메시지 botmsg를 무작위로 뽑습니다. snprintf() 함수가 bot_msg를 msg에 옮깁니다. msg는 클라이언트에게 전송됩니다. snprintf() 함수가 botmsg를 msg로 옮기는 과정에서 FSB가 발생합니다. snprintf()에서 포맷 스트링을 의미하는 세 번째 파라미터에 botmsg가 옵니다. botmsg는 botmsgs에서 선택되므로 bot_msgs를 컨트롤하면 임의의 FSB 공격 페이로드를 실행할 수 있습니다. bot_msgs는 클라이언트가 /addmsg [메시지] 명령어로 컨트롤할 수 있습니다. /addmsg [메시지]명령어를 처리하는 서버 코드는 다음과 같습니다: c if (!memcmp(msg, "/addmsg ", sizeof("/addmsg"))) { memset(botmsgs[i % MAXBOTMSGS], 0x00, BOTMSG_SIZE); strncpy(botmsgs[i++ % MAXBOTMSGS], &msg[8], BOTMSG_SIZE - 1); 따라서 /addmsg [FSB 페이로드]로 bot_msgs를 컨트롤하여 FSB 공격 페이로드를 실행할 수 있습니다. 일반적으로 printf(input);와 같이 간단한 FSB는 컴파일러가 경고 메시지를 띄우지만, 본 문제 소스 코드처럼 컴파일러가 FSB를 잡아내지 못할 수 있습니다. 그만큼 찾기 어려울 수 있으므로 주의 깊게 눈으로 코드를 분석하여 찾거나 포맷 스트링 버그 페이로드를 입력하여 테스트해보면서 찾을 필요가 있습니다. GOT Overwrite FSB를 사용하면 임의 메모리 주소에 임의 값을 덮어쓸 수 있습니다. chatbot_server 바이너리는 PIE (Position Indenpendent Exectuables)가 없고, Partial RELRO (Partial Relocation Read-Only)가 적용되어 있으므로 GOT 영역 주소는 고정이며 쓰기 권한이 있습니다. 따라서 GOT Overwrite를 시도할 수 있습니다. FSB를 사용해서 strcmp()의 GOT를 system()의 GOT로 덮어쓰면, 다음의 /quit 명령어 검증 부분에서 임의 쉘 명령어를 실행할 수 있게 됩니다: c if (read(clnt_sockfd, msg, sizeof(msg)) == -1) PrintError("read() error."); printf("client: %s\n", msg); if (!strcmp(msg, "/quit")) break; Request Bin GOT Overwrite 덕분에 이제 원하는 명령어를 실행할 수 있지만, 명령어의 결과가 stdout(1번 파일 디스크립터)으로 출력됩니다. 소켓 파일 디스크립터로 통신하는 클라이언트는 명령어의 결과를 바로 볼 수 없습니다. 이때 curl 명령어로 플래그를 서버에 전송해서 받아보는 방법으로 문제를 해결할 수 있습니다. 이 방법을 쓰려면 플래그가 담긴 HTTP 요청을 수신할 서버가 필요하지만, Request Bin을 이용하면 개인 서버 없이 요청을 받아볼 수 있습니다. 드림핵 툴즈 서비스에서 Request Bin을 사용할 수 있습니다. File Descriptor Redirection stdin/stdout을 클라이언트 소켓의 파일 디스크립터로 리다이렉션하는 방법으로도 문제를 해결할 수 있습니다. 0번, 1번, 그리고 2번 파일 디스크립터는 순서대로 stdin, stdout, 그리고 stderr가 차지하고 있습니다. 따라서 서버 코드 순서상 서버 소켓의 파일 디스크립터가 3번이고 클라이언트 소켓의 파일 디스크립터가 4번임을 추측할 수 있습니다. sh<&4 >&4;을 system() 인자로 넘겨 실행하면 stdin과 stdout이 클라이언트 소켓의 파일 디스크립터로 리다이렉션된 쉘을 획득합니다. 클라이언트는 이 쉘을 통해 플래그를 구할 수 있습니다. 레퍼런스 https://dreamhack.io/lecture/courses/3 https://dreamhack.io/lecture/courses/66 https://dreamhack.io/lecture/courses/110 https://dreamhack.io/lecture/courses/114 https://tools.dreamhack.games/main
Login Page
Login Page 출제된 CTF: Dreamhack CTF Season 2 Round #4 분야: Web 키워드: Flask, MySQL, SQL Injection, Blind SQL Injection, Improper Session Handling 난이도: ★★☆☆☆ 배경 본 문제는 로그인 검증을 우회하는 SQL Injection 페이로드를 수집하기 위해 만들어진 가상의 로그인 페이지입니다. 본 문제를 해결하려면 Flask와 MySQL로 구현된 웹 애플리케이션을 분석할 수 있는 능력이 필요합니다. 웹 애플리케이션에서 SQL Injection 취약점과 세션을 부적절하게 사용하는 로직 버그를 찾아야 합니다. 발견한 취약점과 버그를 악용하여 Blind SQL Injection을 수행하면 문제를 풀 수 있습니다. 취약한 코드를 분석하고 공격해보며 그 위험성을 몸소 느끼고 안전한 코드를 작성하는 것이 본 문제의 취지입니다. 풀이 프로그램 분석 본 문제는 로그인에 성공하면 플래그가 출력되는 문제입니다. Flask와 MySQL로 구현된 로그인 페이지입니다. 첨부 파일로 주어진 app.py의 login() 함수에 로그인 로직이 구현되어 있습니다. 분석해보면, MySQL 쿼리를 사용한 로그인 검증을 통과해야할 뿐만 아니라 입력한 username/password가 실제 username/password와 동일해야 플래그를 출력한다는 사실을 알 수 있습니다. init.sql을 분석하면 데이터베이스 reset_db가 생성된다는 사실을 알 수 있습니다. 그 안에 테이블 users가 생성됩니다. 테이블에는 admin 계정 정보가 추가됩니다. admin의 비밀번호는 웹 서버가 실행되기 전 인코딩된 urandom 16바이트로 초기화됩니다. 로그인을 5번 실패해도 초기화됩니다. SQL Injection login() 함수에서 MySQL 쿼리를 사용하여 로그인을 검증하는 코드에 SQL Injection 취약점이 존재합니다. 코드는 다음과 같습니다: python ... args = request.form ... username = args['username'] password = args['password'] ... Query the user. done = False while not done: try: query = 'SELECT * FROM users WHERE username = \'{0}\' ' \ 'AND password = \'{1}\'' with lock: query = query.format(username, password) cursor.execute(query) ret = cursor.fetchone() done = True except pymysql.err.InterfaceError: db.close() db, cursor = connect_mysql() ... 공격자가 마음대로 입력할 수 있는 username과 password가 그대로 쿼리에 삽입되어 실행되므로 SQL Injection이 발생합니다. SQL Injection 공격으로 MySQL 쿼리를 사용하는 로그인 검증 로직을 우회할 수 있지만, 입력한 username/password가 실제 username/password와 동일하지 않으므로 플래그를 얻을 수 없습니다. 대신 SQL Injection을 admin의 비밀번호를 알아내는 데에 사용할 수 있을 것입니다. Blind SQL Injection SQL Injection 쿼리가 반환하는 데이터가 공격자 화면에 출력되는 것은 아닙니다. 따라서 Blind SQL Injection 공격이 필요할 수 있습니다. 많은 키워드와 문자를 필터링하지만, OR, IF, SELECT, FROM, WHERE, ORD, MID, AND, BENCHMARK 등을 사용하여 Time-Based SQL Injection을 수행할 수 있습니다. 부적절한 세션 사용 본 웹 애플리케이션에 로그인을 5번 실패하면 admin의 비밀번호가 초기화되는 로직이 있습니다. 이때 세션 단위로 실패 횟수를 체크하므로, A 세션에서 4번 실패하고, B 세션에서 4번 실패하는 경우 비밀번호가 초기화되지 않습니다. 이러한 부적절한 세션 사용 로직을 악용하면 비밀번호를 초기화시키지 않고 SQL Injection을 무제한 시도하여 비밀번호를 알아낼 수 있습니다. 레퍼런스 https://learn.dreamhack.io/166 https://dreamhack.io/lecture/courses/191
Holymoly
해당 풀이는 Link로 이전 되었습니다.
Holymoly
해당 풀이는 Link로 이전 되었습니다.

Have questions about CTF?