Layer7 동아리 과제

웹 해킹 3차시 과제

msh1307 2022. 5. 5. 14:47

XSS-1 소스코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os
 
app = Flask(__name__)
app.secret_key = os.urandom(32)
 
try:
    FLAG = open("./flag.txt""r").read()
except:
    FLAG = "[**FLAG**]"
 
 
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
 
 
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>'
 
 
memo_text = ""
 
 
@app.route("/memo")
def memo():
    global memo_text
    text = request.args.get("memo""")
    memo_text += text + "\n"
    return render_template("memo.html", memo=memo_text)
 
 
app.run(host="0.0.0.0", port=8000)
 
 
cs

XSS-1 풀이


flask로 구현된 웹 사이트를 볼 수 있다.

 

16번째 줄을 보면 read_url 함수는 driver를 통해서 방문하는 사용자가 구현되어있는 것을 알 수 있다.

42번째 줄도 보면 check_xss라는 함수가 urllib.parse.quote를 통해서 파라미터를 넘겨주는 것을 알 수 있다.

그 후 바로 read_url을 호출한다.

58번째 줄에 /flag가 있는데, 메소드는 GET과 POST고 GET일 때, html 리턴하고 POST일 때, 파라미터를 받아서, check_xss에 flag를 넣고 호출해준다.

앞에 not이 붙었으니 함수가 true를 리턴하면 false가 되고 good이 출력된다. 아니라면 wrong이 출력된다. 

 

flag에 들어가보았다. 입력받은 값을 파라미터로 넘겨주는 걸 알 수 있다.

vuln에 파라미터로 넘겨준 값이 <script> alert(1)</script>인데 그냥 아무런 필터링 없이 script가 실행되는 것을 볼 수 있다. 이를 통해서 Reflected XSS가 가능하다고 추측할 수 있다.

개발자 도구에서 본 모습이다.

 

처음에 파라미터로 hello를 넘겨주고 <script>를 써도 동작하지 않는다. 

 

그렇다면 flag를 통해서 document.cookie의 내용을 memo의 파라미터로 넘겨주도록 한다면, flag의 값을 얻을 수 있을 것이다. 

<script> document.location.href='/memo? memo='+document.cookie </script>를 파라미터로 넘겨주면 아래처럼 뜬다.

flag 값을 알 수 있다.

XSS-2 소스코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os
 
app = Flask(__name__)
app.secret_key = os.urandom(32)
 
try:
    FLAG = open("./flag.txt""r").read()
except:
    FLAG = "[**FLAG**]"
 
 
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
 
 
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():
    return render_template("vuln.html")
 
 
@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>'
 
 
memo_text = ""
 
 
@app.route("/memo")
def memo():
    global memo_text
    text = request.args.get("memo""")
    memo_text += text + "\n"
    return render_template("memo.html", memo=memo_text)
 
 
app.run(host="0.0.0.0", port=8000)
 
cs

XSS-2 풀이


전체적으로 코드는 XSS-1과 비슷하다. 53번째 줄 vuln가 조금 바뀌었다. 

vuln에 파라미터를 저렇게 넘겨줘도 아예 작동을 안 한다.

개발자 도구를 통해 본모습이다.

아까와는 다르게 뭐가 조금 추가된 모습이다. 

아마 추가된 것들이 스크립트 실행을 막고 있는 것 같다.

 

memo는 넘겨준 파라미터대로 잘 작동되는 모습을 볼 수 있다. 

 

/flag에서 <img src='' onerror = location.href='/memo? memo='+document.cookie>를 파라미터로 넘겨주었다.

flag를 얻을 수 있다.

 

xss-game level 1 


간단하다. 그냥 script태그 열어주고 alert(1)을 썼다. 

 

xss-game level 2 


올려주고 새로고침 하면 풀린다. <script> 태그가 필터링되어있어서 img와 onerror를 사용했다.

 

xss-game level 3 풀이


코드는 아래 적혀있다. 보면 fragment뒤에 숫자가 바뀌면 그림도 바뀐다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!doctype html>
<html>
  <head>
    <!-- Internal game scripts/styles, mostly boring stuff -->
    <script src="/static/game-frame.js"></script>
    <link rel="stylesheet" href="/static/game-frame-styles.css" />
 
    <!-- Load jQuery -->
    <script
      src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
    </script>
 
    <script>
      function chooseTab(num) {
        // Dynamically load the appropriate image.
        var html = "Image " + parseInt(num) + "<br>";
        html += "<img src='/static/level3/cloud" + num + ".jpg' />";
        $('#tabContent').html(html);
 
        window.location.hash = num;
 
        // Select the current tab
        var tabs = document.querySelectorAll('.tab');
        for (var i = 0; i < tabs.length; i++) {
          if (tabs[i].id == "tab" + parseInt(num)) {
            tabs[i].className = "tab active";
            } else {
            tabs[i].className = "tab";
          }
        }
 
        // Tell parent we've changed the tab
        top.postMessage(self.location.toString(), "*");
      }
 
      window.onload = function() { 
        chooseTab(unescape(self.location.hash.substr(1)) || "1");
      }
 
      // Extra code so that we can communicate with the parent page
      window.addEventListener("message"function(event){
        if (event.source == parent) {
          chooseTab(unescape(self.location.hash.substr(1)));
        }
      }, false);
    </script>
 
  </head>
  <body id="level3">
    <div id="header">
      <img id="logo" src="/static/logos/level3.png">
      <span>Take a tour of our cloud data center.</a>
    </div>
 
    <div class="tab" id="tab1" onclick="chooseTab('1')">Image 1</div>
    <div class="tab" id="tab2" onclick="chooseTab('2')">Image 2</div>
    <div class="tab" id="tab3" onclick="chooseTab('3')">Image 3</div>
 
    <div id="tabContent"> </div>
  </body>
</html>
 
cs

20번째 줄을 보면 num은 window.location.hash인데 window.location.hash를 찾아봤다. 

fragment가 num에 들어간다. 

16번째 줄부터 보면 num이 숫자인지 아닌지 판단도 안 하고 그냥 바로 냅다 parseInt를 하는 것을 볼 수 있다.

그래서 asdf를 fragment로 넘겨주면 Nan(not a number)가 리턴되는 것을 볼 수 있다. 

17번째 줄을 보면 이상한 문자열 처리가 있다.

<img src='/static/level3/cloud문자열.jpg'> 이런 식으로 결합된다.

문자열을 'onerror=alert(1) '이런 식으로 넣어준다면 <img src='/static/level3/cloud'onerror=alert(1) '. jpg'>이렇게 될 것이다. 그리고 띄어쓰기를 해줘서 '. jpg'가 무시되도록 했다. 

XSS challenge stage #1


그냥 이것도 script 태그를 써서 풀었다. 

 

XSS Filtering Bypass  - 소스코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os
 
app = Flask(__name__)
app.secret_key = os.urandom(32)
 
try:
    FLAG = open("./flag.txt""r").read()
except:
    FLAG = "[**FLAG**]"
 
 
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
 
 
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)
 
def xss_filter(text):
    _filter = ["script""on""javascript"]
    for f in _filter:
        if f in text.lower():
            text = text.replace(f, "")
    return text
 
@app.route("/")
def index():
    return render_template("index.html")
 
 
@app.route("/vuln")
def vuln():
    param = request.args.get("param""")
    param = xss_filter(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>'
 
 
memo_text = ""
 
 
@app.route("/memo")
def memo():
    global memo_text
    text = request.args.get("memo""")
    memo_text += text + "\n"
    return render_template("memo.html", memo=memo_text)
 
 
app.run(host="0.0.0.0", port=8000)
 
 
cs

XSS Filtering Bypass  - 풀이


46번째 줄을 보면 script, on, javascript를 필터링하고 있는 것을 확인 할 수 있다. 

그래서 파라미터로 <img src='' oonnerror=locatioonn.href='/memo?memo='+document.cookie>를 넣어주면 on이 ""로 replace되면서 /memo에 쿠키 값을 기록해 줄 수 있다.

플래그를 얻을 수 있다.

Webhacking.kr old - 23번


<script>alert(1)</script>를 입력했다.

no hack이라고 뜨는 것을 확인할 수 있다.

a를 넣었더니 

그대로 출력이 되고, 

aa를 넣었더니 no hack이라고 뜬다.

%0A를 통해서 공백을 넣어줬더니 제대로 출력이 된다.

연속된 문자가 나오면 필터링이 되는 것을 확인할 수 있다.

NULL byte injection을 이용할 것이다. C에서 NULL byte즉 \0는 문자열의 끝을 나타낸다. 이것을 이용해 공격하는 기법이 NULL byte injection이다.

url 인코딩을 통해서 %00을 입력하면 \0을 삽입할 수 있다. 

그래서 %00<%00s%00c%00r%00i%00p%00t%00>%00a%00l%00e%00r%00t%00(%001%00)%00<%00/%00s%00c%00r%00i%00p%00t%00>%00 를 파라미터로 보내주면 풀 수 있다. 

 

'Layer7 동아리 과제' 카테고리의 다른 글

웹 해킹 6차시 과제  (0) 2022.05.19
웹 해킹 4차시 과제  (0) 2022.05.16
웹 해킹 2차시 과제  (0) 2022.05.08
C 4차시 과제  (0) 2022.04.16
C 3차시 과제  (0) 2022.04.12