PatriotCTF 2024 : Really Only Echo

shrew·2024년 9월 23일

PatriotCTF 2024

목록 보기
1/2
post-thumbnail

Really Only Echo

2024년 9월 21일부터 진행된 PatriotCTF의 misc 분야로 출제된 문제였다. 문제 내용을 번역하자면 '안녕하세요, echo만 사용하는 터미널을 만들었는데, flag를 찾을 수 있나요?' 라는 내용이었다.

Write-up

nc 서버에 접속해보면 위와 같이 명령어를 칠 수 있는 터미널이 나온다.
실제로 echo 명령어는 잘 실행되지만 다른 명령어를 사용하면 'HEY! I said only echo works.' 라는 문자열을 출력하고 명령어를 실행하지 않는다.
여러 명령어들을 시도해보면서 알아낸 것은 echo 명령어를 제외한 명령어들이 전부 필터링 되어 있는 것 같았으나 파이프라인이나 && 같은 연산자들은 필터링이 되어있지 않았다. 따라서 연산자 뒤에 명령어를 명령어로 인식하지 못하게 한다면 flag를 찾을 수 있을 것이다.
따라서 특수 문자 이스케이프를 사용하면 되겠다고 생각했고, 실제로 그렇게 해서 해당 문제를 풀 수 있었다.

특수 문자 이스케이프

특별한 의미를 가지는 문자 앞에 역슬래쉬(\)를 붙이면 해당 문자를 일반 문자로 인식하게 되는 기능이다. 보통은 '|', '&' 등과 같은 특수 문자 앞에 역슬래쉬를 붙여서 사용하지만 명령어 앞에 붙여서 사용할 수 있다.
명령어 앞에 역슬래쉬를 붙이게 되면 해당 명령어에 대해 설정된 별칭(alias)이나 함수(function)를 무시하고 원래의 기본 명령어를 실행하도록 할 수 있다.

그러나 CTF가 모두 끝나고 해당 문제의 서버 파이썬 코드가 올라왔다.

#!/usr/bin/python3

import os,pwd,re
import socketserver, signal
import subprocess

listen = 3333

blacklist = os.popen("ls /bin").read().split("\n")
blacklist.remove("echo")
#print(blacklist)

def filter_check(command):
    user_input = command
    parsed = command.split()
    #Must begin with echo
    if not "echo" in parsed:
        return False
    else:
        if ">" in parsed:
            #print("HEY! No moving things around.")
            req.sendall(b"HEY! No moving things around.\n\n")
            return False
        else:
            parsed = command.replace("$", " ").replace("(", " ").replace(")", " ").replace("|"," ").replace("&", " ").replace(";"," ").replace("<"," ").replace(">"," ").replace("`"," ").split()
            #print(parsed)
            for i in range(len(parsed)):
                if parsed[i] in blacklist:
                    return False
            return True

def backend(req):
    req.sendall(b'This is shell made to use only the echo command.\n')
    while True:
        #print("\nThis is shell made to use only the echo command.")
        req.sendall(b'Please input command: ')
        user_input = req.recv(4096).strip(b'\n').decode()
        print(user_input)
        #Check input
        if user_input:
            if filter_check(user_input):
                output = os.popen(user_input).read()
                req.sendall((output + '\n').encode())
            else:
                #print("Those commands don't work.")
                req.sendall(b"HEY! I said only echo works.\n\n")
        else:
            #print("Why no command?")
            req.sendall(b"Where\'s the command.\n\n")

class incoming(socketserver.BaseRequestHandler):
    def handle(self):
        signal.alarm(1500)
        req = self.request
        backend(req)


class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass


def main():
    uid = pwd.getpwnam('ctf')[2]
    os.setuid(uid)
    socketserver.TCPServer.allow_reuse_address = True
    server = ReusableTCPServer(("0.0.0.0", listen), incoming)
    server.serve_forever()

if __name__ == '__main__':
    main()

위는 해당 파이썬 코드이다. 우리가 자세히 봐야할 부분은 해당 코드의 특수 문자 필터링을 수행하는 코드이다.

        else:
            parsed = command.replace("$", " ").replace("(", " ").replace(")", " ").replace("|"," ").replace("&", " ").replace(";"," ").replace("<"," ").replace(">"," ").replace("`"," ").split()
            #print(parsed)
            for i in range(len(parsed)):
                if parsed[i] in blacklist:
                    return False
            return True

위는 특수 문자 필터링을 수행하는 코드만 따로 빼온 코드이다. 살펴보면 '$', '(', ')' 등 대부분의 특수 문자를 금지하고 있다. 그러나 '\'는 필터링 대상에 들어가 있지 않는 것을 볼 수 있다. 따라서 cat 명령어 앞에 역슬래쉬를 넣었을 때 필터링이 되지 않았던 것 같다.


FLAG :
profile
보안 공부 로그

0개의 댓글