Bugku CTF web39(Web)

Scan the directory, get .index.php.swp to
Directory scan results
download the file, use vim -r .index.php.swp to open the audit source code. Comments have been manually added to the code

<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();

function get_random_iv(){	//随机生成16位初始化向量
    $random_iv='';
    for($i=0;$i<16;$i++){
        $random_iv.=chr(rand(1,255));
    }
    return $random_iv;
}

#第一个执行的方法
function login($info){
    $iv = get_random_iv();
    $plain = serialize($info);	//明文序列化
    $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);	//加密
	//options:以下标记的按位或: OPENSSL_RAW_DATA 原生数据,对应数字1,不进行 base64 编码。OPENSSL_ZERO_PADDING 数据进行 base64 编码再返回,对应数字0。 
    $_SESSION['username'] = $info['username'];	//注册SESSION全局变量
	//以下两行设置cookie
    setcookie("iv", base64_encode($iv));
    setcookie("cipher", base64_encode($cipher));
}

function check_login(){
    if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
        $cipher = base64_decode($_COOKIE['cipher']);
        $iv = base64_decode($_COOKIE["iv"]);
        if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
            $info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
            $_SESSION['username'] = $info['username'];
        }else{
            die("ERROR!");
        }
    }
}

#第二个执行,检测用户名为admin时,打印flag
function show_homepage(){
    if ($_SESSION["username"]==='admin'){
        echo '<p>Hello admin</p>';
        echo '<p>Flag is $flag</p>';
    }else{
        echo '<p>hello '.$_SESSION['username'].'</p>';
        echo '<p>Only admin can see flag</p>';
    }
    echo '<p><a href="loginout.php">Log out</a></p>';
}

if(isset($_POST['username']) && isset($_POST['password'])){
    $username = (string)$_POST['username'];
    $password = (string)$_POST['password'];
    if($username === 'admin'){
        exit('<p>admin are not allowed to login</p>');
    }else{
        $info = array('username'=>$username,'password'=>$password);
        login($info);
        show_homepage();
    }
}else{
    if(isset($_SESSION["username"])){
        check_login();
        show_homepage();
    }else{
        echo '<body class="login-body">
                <div id="wrapper">
                    <div class="user-icon"></div>
                    <div class="pass-icon"></div>
                    <form name="login-form" class="login-form" action="" method="post">
                        <div class="header">
                        <h1>Login Form</h1>
                        <span>Fill out the form below to login to my super awesome imaginary control panel.</span>
                        </div>
                        <div class="content">
                        <input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
                        <input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
                        </div>

 

Source code audit

To audit source code, you must first find the starting point of the program, follow the program to understand the process.
The starting point of the program is in this if:
Insert picture description here

We use else as the delimiter, first look at the code in the above paragraph.
The program receives POST parameters (username, password) and prohibits admin from logging in. When the user name is not admin, first put the user name and password into the array and pass it to the login method.
The login method serializes the incoming array and uses aes-128-cbc to encrypt the serialization. iv (initialization vector) is randomly generated. Finally put the cipher and iv into the cookie.
Insert picture description here

Then to the show_homepage() method, when the username in $_SESSION is detected as admin, the flag is printed. Otherwise prompt Only admin can see flag
Insert picture description here

Then audit the lower part of the else, here is the execution when $_SESSION['username'] exists after the upper part of the operation is executed. When there is no POST data or $_SESSION['username'], the login page is displayed.
When there is $_SESSION['username'], enter the check_login() method.
When there are cipher and iv in the cookie, decrypt the cipher. Here is the key to solving the problem. You can modify the user name of the serialized data to admin by modifying the cipher value in the cookie. Thereby bypassing the judgment that admin login is prohibited at the beginning of the program.
Insert picture description here

Finally, execute the show_homepage() method. When we change the user name to admin in check_login(), the flag is output here.

Problem solving:
visit the problem page and log in with the username admil and password 123. The content of the page prompt is consistent with the audit result. At this point, the program has executed the login() method, and has written cipher and iv in the cookie.


Use burp to capture the package and refresh the page. The content is as follows:
According to the above audit source code, you need to delete the post data to enter the check_login() method to determine the current user name.

Through the "CBC Byte Flip Attack Principle" article listed at the beginning, here you need to modify the values ​​of cipher and iv to change the user name.

Basic principles (strong content):

Insert picture description here

Here's why we can modify admil to admin.
According to the above figure, we can know the CBC decryption process:

Ciphertext 1=>decrypted ciphertext 1=>decrypted ciphertext 1 XOR initialization vector (iv) = plaintext 1
ciphertext 2=>decrypted ciphertext 2=>decrypted ciphertext 2 XOR ciphertext 1 = plaintext 2
ciphertext 3= >Decrypted ciphertext 3=>Decrypted ciphertext 3 XOR ciphertext 2 = plaintext 3
and so on, except for the first time, all subsequent data decryption need to be XORed with the previous ciphertext to obtain the plaintext.
It can be inferred from the above decryption process that when we modify the Nth byte of the previous ciphertext, it will affect the Nth byte of the plaintext decrypted by the latter ciphertext.
For example: when we modify the 6th byte of ciphertext 1, when ciphertext 2 is decrypted, the decrypted ciphertext 2 is XORed with ciphertext 1, and the 6th byte of plaintext 2 will also be affected.
XOR feature:
The step of decrypting to get the plaintext uses XOR operation, and XOR operation has a feature that you can customize the XOR result.
The explanation here borrows from the explanation ideas of the gangster article.

 

假设:A ^ B = C,则可得
B = A ^ C
当人为修改A=A ^ C时,
A ^ B = A ^ C ^ B = B ^ B = 0
当人为修改A=A ^ C ^ x (x为任意数值)时,
A ^ B = A ^ C ^ x ^ B = B ^ B ^ x = x

举例:
密文1[4]的意思是密文1字符串第4个字节,相当于数组下标。
设:密文1[4] = A,解密(密文2)[4] = B,明文2[4] = C
因为A ^ B = C,根据结论有B = A ^ C
当人为修改A=A ^ C时,那么A ^ B = A ^ C ^ B = B ^ B = 0,这样明文2[4]的结果就为0了
当人为修改A=A ^ C ^ x (x为任意数值)时,那么
A ^ B = A ^ C ^ x ^ B = B ^ B ^ x = x,这是明文2[4] = x,这样就达到了控制明文某个字节的目的了。

Programming:
According to the above deduction, you can start to write a program to modify the cipher and iv to control the user name.

<?php
header("Content-Type: text/html;charset=utf-8");
	#计算cipher
	/*
	明文1:a:2:{s:8:"userna		//r
	明文2:me";s:5:"admil";		//l字母在第14个字节
	明文3:s:8:"password";s
	明文4::3:"123";}
	*/
	$cipher = base64_decode(urldecode('f0csYAAWDy%2FGlSsvWLr6NlCBad4p2U%2BXm2Rr2X07iytKd4r8V5tbO7%2FcxIib96eRDGUOMQclQgvxw2SZXOobWQ%3D%3D'));
	$temp = $cipher;
	/*
	设密文1[13]=A,	解密(密文2)[13]=B,	明文2[13]=C,	
	将A修改为A ^ C,则:
	A ^ B = A ^ C ^ B = B ^ B = 0 = C
	*/
	//						A				  C			 X
	$cipher[13] = chr(ord($cipher[13]) ^ ord('l') ^ ord('n'));
	echo urlencode(base64_encode($cipher));
	?>

 

 

Results of the:

Insert picture description here
When we replaced and sent the calculated cipher in burp, an error was found.
This is because we modified the data in ciphertext 1, which made the plaintext data decrypted for the first time out of order, disrupted the format of the serialized data, and the deserialization failed.
But when we decode the returned base64 data, we can find that our username has been changed to admin.

Insert picture description here

Insert picture description here

When this kind of error occurs, according to the CBC decryption principle, we only need to modify the value of iv so that iv XOR decryption (ciphertext 1) = plaintext 1 is enough.
Write the program to achieve: 

<?php
	#计算iv
	$res = base64_decode('yeiydaYLG5RNzOPWaQgOkG1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjM6IjEyMyI7fQ==');	//这里放burp放回的base64数据
	$iv = base64_decode(urldecode('TD9FI%2FvbrZn%2FSjbSD9bfSQ%3D%3D')); //这里放cookie中的iv
	$plaintext = 'a:2:{s:8:"userna';
	$new_iv = '';
	for ($i = 0; $i < 16; $i ++){
		$new_iv = $new_iv . chr(ord($iv[$i]) ^ ord($res[$i]) ^ ord($plaintext[$i]));
	}
	echo urlencode(base64_encode($new_iv));
?>

Results of the:
Insert picture description here

In burp, replace iv and go. Get the flag.
It should be noted that every time a data packet is sent, the cipher and iv will change, so the previous steps should be done in one go~
Insert picture description here

Guess you like

Origin blog.csdn.net/ChaoYue_miku/article/details/115096330