Palu2024_reverse

PaluCTF 2024

总结:Reverse只拿了3个一血,排名吊车尾,还是太菜了,其他的不是很会做

在这里插入图片描述

Reverse-签到

方法一:
DFS启动,喜提8192种结果,这8192种结果经过原exe加密后都能得到与原encrypted.txt一模一样的内容,其中flag{}格式的有256种,那么接下来只需爆破平台即可 (汗)

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
typedef struct TABLE {
	bool isvisit;
	unsigned char type;
	int val;
	int MAX_size;
}TABLE,* PTABLE;

typedef struct ENC {
	unsigned char s[256];
}ENC;

int n;

PTABLE Init_table(int MAX_size);
void Insert_table(PTABLE table, unsigned char New_type, int New_val);
void Inserts(PTABLE Table);
int Findval(PTABLE Table, unsigned char Nval);
void DFS(PTABLE Table, ENC now, int index, int len, unsigned char* cipher);
void RecoverString(PTABLE Table, unsigned char* new_cipher);

unsigned char cipher[] = "jmdiz61904646906034535196{";

int main()
{
	PTABLE Table = Init_table(15);
	Inserts(Table);
	RecoverString(Table, cipher);
}



PTABLE Init_table(int MAX_size)
{
	PTABLE New_table = (PTABLE)malloc(sizeof(TABLE) * MAX_size);
	for (int i = 0; i < MAX_size; i++)
	{
		New_table[i].isvisit = false;
		New_table[i].type = NULL;
		New_table[i].val = 0;
		New_table[i].MAX_size = MAX_size;
	}
	return New_table;
}
void Insert_table(PTABLE table, unsigned char New_type, int New_val)
{
	int index = 0;
	int Max_size = table->MAX_size;
	while (1)
	{
		PTABLE PNow = table + index;
		if (!PNow->isvisit)
		{
			PNow->isvisit = true;
			PNow->type = New_type;
			PNow->val = New_val;
			return;
		}
		else {
			index++;
		}
		if (index >= Max_size)
		{
			return;
		}
	}
}
void Inserts(PTABLE Table)
{
	Insert_table(Table, 'a', 3);
	Insert_table(Table, 'l', 1);
	Insert_table(Table, 'f', 4);
	Insert_table(Table, 'g', 2);
	Insert_table(Table, '{', -1);
	Insert_table(Table, '}', -2);
	Insert_table(Table, '1', 5);
	Insert_table(Table, '2', 7);
	Insert_table(Table, '3', -3);
	Insert_table(Table, '4', -3);
	Insert_table(Table, '5', -3);
	Insert_table(Table, '6', -3);
	Insert_table(Table, '7', -3);
	Insert_table(Table, '8', -3);
	Insert_table(Table, '9', -3);
}
int Findval(PTABLE Table, unsigned char Nval)
{
	int index = 0;
	int Max_size = Table->MAX_size;
	while (1)
	{
		PTABLE PNow = Table + index;
		if (PNow->type == Nval)	return PNow->val;
		else index++;
		if (index >= Max_size)	return 0;
	}
}

void DFS(PTABLE Table, ENC now, int index, int len, unsigned char* cipher)
{
	if (index >= len)
	{
		n++;
		printf("%d: ", n);
		for (int i = 0; i < len; i++)
		{
			printf("%c", now.s[i]);
		}
		printf("\n");
		return;
	}
	unsigned char tmp;
	ENC next;
	for (int i = 0; i < index; i++)
	{
		next.s[i] = now.s[i];
	}
	for (int k = 32; k <= 127; k++)
	{
		tmp = k + Findval(Table, k);
		if (tmp == cipher[index])
		{
			next.s[index] = k;
			DFS(Table, next, index + 1, len, cipher);
		}
	}
}

void RecoverString(PTABLE Table, unsigned char* new_cipher)
{
	n = 0;
	int len_str = strlen(new_cipher);
	ENC Origin;
	memset(Origin.s, 0, len_str);
	DFS(Table, Origin, 0, len_str, new_cipher);
}

方法二:PY

IDA打开,可以看到非常人畜无害的加密流程,而熟悉c++逆向的同学都知道,主程序里除了enc函数外都是些没啥卵用的转换函数,不用管,那就让我们点进去康康。

在这里插入图片描述

一打开人直接头晕了,半点"茶(TEA)"的样子都看不来,不过看着前面的一堆也只是移位之类的操作,那就不管了,直接定位到关键的encs加密,点进去康康

在这里插入图片描述

由于场面太过血腥暴力我就不放encs函数的内部了,总之就是看不太懂,这时直接祭出Findcrypt大法,查找到一个叫做"salsa20"加密的标识,然后跟进去发现了一串base64密文,解出来说是Salsa20升级版

在这里插入图片描述

既然是Salsa20的升级版,那就先了解一下这个Salsa20先,百度一搜,嗯?居然是流密码!!!升级版再怎么样也不会改变加密的性质吧,因此可以基本确定是加密是流密码。

经典的流密码有什么?RC4!

那也就意味着排除反调试的情况下,只要保持key等参数不变,那么只需动调取出最后与明文交汇的值便可,就算有反调试也可借助x64dbg强大的ScyllaHide插件直接绕过

在这里插入图片描述

事不宜迟,动调,启动!随便输入一个flag,找到关键的异或点,然后取出所有的异或值,然后直接运行到最终的比较函数

在这里插入图片描述

然后发现这样一串奇怪的密文,根据我们输入的flag不难发现规律:

如果加密后得到0x12, 0x34 , 0xAB ,0xCD,那么密文则转化为字符串"1234ABCD"。这样我们的密文就应该是0xf5, 0x68, 0xc4 …这样了,接下来就是写一个脚本梭哈。

在这里插入图片描述

EXP:

#include<iostream>
using namespace std;
unsigned char key[] =
{
  0x93, 0x04, 0xA5, 0xEE, 0x69, 0xAC, 0xA3, 0xA8, 0x0D, 0x45, 
  0x2E, 0x28, 0x9D, 0x20, 0x06, 0x27, 0xA2, 0xB8, 0xB2, 0xA1, 
  0x20, 0xF0, 0x7D, 0x98, 0xDB, 0x36, 0x2F, 0x28, 0x76, 0x62, 
  0x43, 0xF7, 0xED, 0x8F, 0x4D, 0x69, 0xD2, 0xA5, 0xCD, 0x1C, 
  0x24, 0x81, 0x96, 0x2B, 0xC8, 0x2E, 0x12, 0x34, 0x8B, 0x94, 
  0x8E, 0x09, 0xBD, 0x28, 0x4F, 0x0D, 0x05, 0xB0, 0xA7, 0xF6, 
  0x4F, 0x8C, 0xC5, 0x61, 0x93, 0x04, 0xA5, 0xEE, 0x69, 0xAC, 
  0xA3, 0xA8, 0x0D, 0x45, 0x2E
}; 
unsigned char enc[]="f568c48912eed6dc520c7164f44b6378e1d0d3e248914fa8847b405a131f";
unsigned char encs[]={0xf5,0x68,0xc4,0x89,0x12,0xee,0xd6,0xdc,0x52,0x0c,0x71,0x64,0xf4,0x4b,0x63,0x78,0xe1,0xd0,0xd3,0xe2,0x48,0x91,0x4f,0xa8,0x84,0x7b,0x40,0x5a,0x13,0x1f};
int main()
{
    unsigned char a;
    for(int i=0;i<30;i++)
    {
        a=encs[i]^key[i];

    printf("%c",a);
    }
}//flag{But_I_Like_ChaCha20_More}

哎哟好像真是个flag,交上去就行了(

我们可以发现flag的内容为ChaCha20,也就是Salsa20的升级版,好家伙,我是真不会。

这里给出出题人的预期exp,一个标准的chacha20算法,可以好好学习一下

#include <iostream>
#include <string>
#include <vector>
#include <bits/stdc++.h>
// 字节序转换
#define U8TO32_LITTLE(p) \
    (((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
     ((uint32_t)((p)[2]) << 16) | ((uint32_t)((p)[3]) << 24))

// 四分之一轮
void quarter_round(uint32_t *state, int a, int b, int c, int d) {
    state[a] += state[b];
    state[d] ^= state[a];
    state[d] = (state[d] << 16) | (state[d] >> 16);
    state[c] += state[d];
    state[b] ^= state[c];
    state[b] = (state[b] << 12) | (state[b] >> 20);
    state[a] += state[b];
    state[d] ^= state[a];
    state[d] = (state[d] << 8) | (state[d] >> 24);
    state[c] += state[d];
    state[b] ^= state[c];
    state[b] = (state[b] << 7) | (state[b] >> 25);
}

void chacha20_block(uint32_t *state, unsigned char *output) {
    uint32_t working_state[16];
    for (int i = 0; i < 16; ++i)
        working_state[i] = state[i];
    for (int i = 0; i < 10; i++) {
        // 列轮
        quarter_round(working_state, 0, 4,  8, 12);
        quarter_round(working_state, 1, 5,  9, 13);
        quarter_round(working_state, 2, 6, 10, 14);
        quarter_round(working_state, 3, 7, 11, 15);
        // 对角轮
        quarter_round(working_state, 0, 5, 10, 15);
        quarter_round(working_state, 1, 6, 11, 12);
        quarter_round(working_state, 2, 7,  8, 13);
        quarter_round(working_state, 3, 4,  9, 14);
    }
    for (int i = 0; i < 16; ++i) {
        state[i] += working_state[i];
        uint32_t value = state[i];
        output[4 * i + 0] = (unsigned char)(value & 0xff);
        output[4 * i + 1] = (unsigned char)((value >> 8) & 0xff);
        output[4 * i + 2] = (unsigned char)((value >> 16) & 0xff);
        output[4 * i + 3] = (unsigned char)((value >> 24) & 0xff);
    }
}
std::vector<unsigned char> chacha20_decrypt(const std::vector<unsigned char> &ciphertext, const std::string &key,

        const std::string &nonce) {
    // 初始化状态矩阵
    uint32_t state[17] = {
        0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, // 常量
        U8TO32_LITTLE(key.data()), U8TO32_LITTLE(key.data() + 4),
        U8TO32_LITTLE(key.data() + 8), U8TO32_LITTLE(key.data() + 12),
        U8TO32_LITTLE(key.data() + 16), U8TO32_LITTLE(key.data() + 20),
        U8TO32_LITTLE(key.data() + 24), U8TO32_LITTLE(key.data() + 28),
        0, 0, // 计数器
        U8TO32_LITTLE(nonce.data()), U8TO32_LITTLE(nonce.data() + 4),
        U8TO32_LITTLE(nonce.data() + 8)
    };

    std::vector<unsigned char> plaintext;
    // 分块解密
    for (size_t i = 0; i < ciphertext.size(); i += 64) {
        unsigned char block[64];
        chacha20_block(state, block);
        // 计数器加 1
        state[12]++;
        // 密钥流异或
        for (size_t j = 0; j < 64 && i + j < ciphertext.size(); ++j) {
            plaintext.push_back(ciphertext[i + j] ^ block[j]);
        }
    }

    return plaintext;
}

int main() {
    std::vector<unsigned char> cipher = {
        0xf5, 0x68, 0xc4, 0x89, 0x12, 0xee, 0xd6, 0xdc, 0x52, 0x0c, 0x71, 0x64, 0xf4, 0x4b, 0x63, 0x78, 0xe1, 0xd0, 0xd3, 0xe2, 0x48, 0x91, 0x4f, 0xa8, 0x84, 0x7b, 0x40, 0x5a, 0x13, 0x1f
    };
    std::string key = "SGludDogSW1wcm92ZSBvZiBTYWxzYTIw";
    std::string nonce = "Is_This_TEA?";

    std::vector<unsigned char> test = chacha20_decrypt(cipher, key, nonce);

    std::string decrypted_message(test.begin(), test.end());

    std::cout << "Decrypted message: " << decrypted_message << std::endl;
}

然后给出yuro哥哥的梭哈脚本

from Crypto.Cipher import ChaCha20

enc_data = bytes.fromhex("f568c48912eed6dc520c7164f44b6378e1d0d3e248914fa8847b405a131f")


key = b"SGludDogSW1wcm92ZSBvZiBTYWxzYTIw"
nonce = b"Is_This_"

cc = ChaCha20.new(key=key, nonce=nonce)

print(cc.decrypt(enc_data))

Auth_System

最良心的一道题目了,真-运行就有flag,ida打开,用Graph看,发现左边流程部分没有被识别出来,正常流程走的也是右边,那就patch一下进去康康,把jz改成jnz

在这里插入图片描述

好家伙,直接把flag给爆出来了

在这里插入图片描述

O2 Optimization

不太懂为啥这题的解题数这么少,可能是大家把他当异架构做了?

DIE康康,发现啥也没识别出来,但清清楚楚地写着架构为AMD64,就是IDA64打开直接一顿猛猛报错,然后也是一副非常诡异的画面

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

这里根据经验便可猜测是ELF的头被修改了,我们尝试与正确的ELF文件头作比较,最后发现一处关键的修改点。
简单了解一下ELF结构:
圈红处字节代表着版本,0x1为ELF32,0x2为ELF64,而我们的程序架构为AMD64,所以这里应该为0x2,修改然后保存,再用DIE查看发现正常了。

在这里插入图片描述

在这里插入图片描述

然后ida打开正常,但main()函数无法f5,提示是在0x2356处出了问题,我们定位到次处,点开sub_26C0这个函数,发现并没有关键的加密部分,那就先nop掉
在这里插入图片描述

f5成功后,看到又是个C++程序,直接定位到关键enc()加密函数

在这里插入图片描述

c++有很多不明所以的东西,靠经验判断是最快的捷径

在这里插入图片描述

这里可以直接猜测加密流程为

for(int i=0;i<len;i++)
{
    input[i] = (input[i] + key[i % key_len])%0x80;
}

然后密文和key值从这里来,密文的方式与茶那题是一样的
在这里插入图片描述

exp

#include<iostream>
using namespace std;
unsigned char encs[]="364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643";
unsigned char enc[]={0x36,0x4d,0x4d,0x5c,0x3e,0x38,0x7e,0x00,0x42,0x1c,0x59,0x7a,0x0a,0x73,0x02,0x14,0x4d,0x5b,0x70,0x08,0x7e,0x06,0x46,0x19,0x56,0x73,0x36,0x29,0x7d,0x15,0x1f,0x56,0x77,0x0a,0x79,0x35,0x42,0x4f,0x2a,0x78,0x06,0x43};
unsigned char key[]="PaluCTF";
int main()
{
/*    for(int i=0;i<sizeof(encs)-1;i+=2)
    {
        printf("0x%c%c,",encs[i],encs[i+1]);
    }*/
    for(int i=0;i<sizeof(enc);i++)
    {
        for(int k=32;k<=127;k++)
        {
            if(((k+key[i%7])%0x80)==enc[i])
            {
                printf("%c",k);
            }
        }

//printf("\n");
    }
}
//flag{d80a0d76-23af-486e-a0bc-43a463eac552}

PyLu

方法一:摇一个密码✌

方法二:yuro哥哥的z3 solver法

import z3
from Crypto.Util.number import *


def enc(key):
    R = bytes_to_long(b"Welcome To PaluCTF!")
    MOD = 2**418
    R = R ^ ((R - 60) >> 24)
    R = R ^ ((R - 60) << 88)
    R ^= key
    R = (-R * R * 2024) % MOD
    R = (R * key) % MOD
    return R


res = 0x2E441F765514CCA89173554726494D37E9FBE774B6F807BC5F6E71117530CE3D7DB5F70554C03CD9055F4E42969600904DF1F4DB8
s = z3.Solver()
key = z3.BitVec("key", 418)
s.add(enc(key) == res)
s.check()
m = s.model()
flag = long_to_bytes(m[key].as_long())
tes(m[key].as_long())
print(flag)

帕鲁被病毒感染了

方法一:摇一个misc✌
方法二:PY

  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值