[GUET-CTF2019]encrypt

小白一枚,膜拜大佬,欢迎指正

总结:

  • RC4加密
  • ida远程动调

[GUET-CTF2019]encrypt

exeinfope看一下,是64位无壳程序
在这里插入图片描述
导入ida64分析,贴上主函数伪代码:

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  unsigned int v3; // eax
  unsigned int v4; // eax
  char v6[4]; // [rsp+4h] [rbp-93Ch] BYREF
  int i; // [rsp+8h] [rbp-938h]
  int v8; // [rsp+Ch] [rbp-934h]
  _DWORD v9[260]; // [rsp+10h] [rbp-930h] BYREF
  char v10[16]; // [rsp+420h] [rbp-520h] BYREF
  char s[256]; // [rsp+430h] [rbp-510h] BYREF
  char v12[1032]; // [rsp+530h] [rbp-410h] BYREF
  unsigned __int64 v13; // [rsp+938h] [rbp-8h]

  v13 = __readfsqword(0x28u);
  v10[0] = 16;
  v10[1] = 32;
  v10[2] = 48;
  v10[3] = 48;
  v10[4] = 32;
  v10[5] = 32;
  v10[6] = 16;
  v10[7] = 64;
  memset(s, 0, sizeof(s));
  v8 = strlen(s);
  memset(v12, 0, 0x400uLL);
  printf("please input your flag:");
  scanf("%s", s);
  memset(v9, 0, 0x408uLL);
  sub_4006B6(v9, v10, 8);                       // 动态调试获取v9的值
  v3 = strlen(s);
  sub_4007DB(v9, s, v3);
  v4 = strlen(s);
  sub_4008FA(s, v4, v12, v6);
  for ( i = 0; i <= 50; ++i )
  {
    if ( v12[i] != byte_602080[i] )
    {
      puts("Wrong");
      return 0LL;
    }
  }
  puts("Good");
  return 0LL;
}

整体来说很直接的一道题,考察算法逆向分析能力。
先来看第一个函数sub_4006B6()

bool __fastcall sub_4006B6(_DWORD *a1, __int64 a2, int a3)
{
  bool result; // al
  int i; // [rsp+1Ch] [rbp-18h]
  int j; // [rsp+1Ch] [rbp-18h]
  int v6; // [rsp+20h] [rbp-14h]
  int v7; // [rsp+24h] [rbp-10h]
  int v8; // [rsp+28h] [rbp-Ch]
  _DWORD *v9; // [rsp+2Ch] [rbp-8h]

  *a1 = 0;
  a1[1] = 0;
  v9 = a1 + 2;
  for ( i = 0; i <= 255; ++i )
    v9[i] = i;
  v7 = 0;
  result = 0;
  LOBYTE(v6) = 0;
  for ( j = 0; j <= 255; ++j )
  {
    v8 = v9[j];
    v6 = (v6 + v8 + *(v7 + a2));
    v9[j] = v9[v6];
    v9[v6] = v8;
    result = ++v7 >= a3;
    if ( v7 >= a3 )
      v7 = 0;
  }
  return result;
}

获取v9的值对后面的解密起到了至关重要的作用,我们可以直接linux下ida动态调试, 在return result;指令处下断点,然后F9运行到断点处,双击v9进入栈窗口可以获取v9内的值。

【后来学习别的博主的wp知道了这几个函数是RC4加密,这个函数是RC4算法里的生成S盒,再打乱S盒】
在这里插入图片描述

    0x00, 0x00, 0xB0, 0x31, 0x75, 0x70, 0xF8, 0xDF,
    0x07, 0x3C, 0x78, 0x71, 0x50, 0x29, 0x2C, 0x16,
    0x69, 0x12, 0xC8, 0x2B, 0x3B, 0x7F, 0xB2, 0xE7,
    0x4B, 0x68, 0x8C, 0xC5, 0xA6, 0x15, 0x03, 0x58,
    0x47, 0x04, 0x13, 0x8D, 0x87, 0x26, 0x09, 0xED,
    0x17, 0x8A, 0xC2, 0xF2, 0x43, 0xC0, 0xAC, 0x59,
    0x97, 0xF5, 0x3F, 0x67, 0x5E, 0x39, 0x86, 0xD5,
    0x72, 0x61, 0xDA, 0xF7, 0x01, 0x05, 0x8B, 0xC3,
    0xB1, 0x77, 0xAF, 0x1D, 0x30, 0xC6, 0x45, 0x0E,
    0x5F, 0xEE, 0xAE, 0xF0, 0x28, 0xCE, 0xCD, 0xA7,
    0x9B, 0x2A, 0x19, 0x48, 0x08, 0x44, 0x20, 0xFE,
    0x6D, 0xB5, 0x2E, 0x6A, 0xF1, 0x34, 0xBC, 0x1E,
    0x3E, 0xCC, 0x41, 0x92, 0xD8, 0xBD, 0xA5, 0xE8,
    0x4D, 0x0A, 0x49, 0x0D, 0xA2, 0xFA, 0x62, 0x74,
    0xD4, 0x83, 0x96, 0x94, 0x3D, 0xCB, 0x18, 0x63,
    0x99, 0x46, 0xCA, 0xB7, 0x8E, 0xCF, 0xFB, 0xA3,
    0x6C, 0x7E, 0x51, 0x27, 0x60, 0x9A, 0x11, 0xF3,
    0x5C, 0x6E, 0xBA, 0x42, 0x76, 0x2F, 0xEF, 0xBF,
    0x21, 0xAA, 0xE4, 0xD6, 0x1B, 0x55, 0x7D, 0xBE,
    0xEA, 0xD3, 0x10, 0xF4, 0xC7, 0x4A, 0x23, 0x79,
    0x84, 0xA4, 0x1C, 0xAB, 0x14, 0xDB, 0x4C, 0x3A,
    0xB8, 0x52, 0xEC, 0x37, 0x38, 0xB6, 0xD2, 0xA0,
    0x5A, 0x5B, 0x98, 0x66, 0x54, 0x9E, 0x4E, 0x4F,
    0xB4, 0xC4, 0xC9, 0xD0, 0x25, 0x9C, 0x80, 0xDE,
    0x2D, 0x06, 0x22, 0x0B, 0x91, 0x6B, 0x9F, 0xF6,
    0xE6, 0xE2, 0xC1, 0x0F, 0x93, 0x90, 0x7B, 0x9D,
    0x8F, 0xDD, 0xE5, 0x65, 0x35, 0xAD, 0xA9, 0xDC,
    0x82, 0xBB, 0x00, 0x53, 0xD1, 0xA8, 0x33, 0xE9,
    0x40, 0x1A, 0xFF, 0xA1, 0x95, 0x36, 0xD9, 0xEB,
    0x89, 0xE3, 0x7C, 0x73, 0x85, 0x88, 0x7A, 0xE0,
    0xFD, 0x64, 0x0C, 0x57, 0x32, 0xB3, 0xB9, 0x1F,
    0xD7, 0xFC, 0x81, 0xE1, 0x02, 0xF9, 0x5D, 0x56,
    0x6F, 0x24

接下来我们看sub_4007DB函数,这个函数主要是对上面的v9进行了一些更改,然后把输入的字符串每一个字符都与改过的v9进行异或。

_DWORD *__fastcall sub_4007DB(_DWORD *a1, __int64 a2, int a3)
{
  _DWORD *result; // rax
  int i; // [rsp+18h] [rbp-1Ch]
  int v5; // [rsp+1Ch] [rbp-18h]
  int v6; // [rsp+20h] [rbp-14h]
  int v7; // [rsp+24h] [rbp-10h]
  int v8; // [rsp+28h] [rbp-Ch]
  _DWORD *v9; // [rsp+2Ch] [rbp-8h]

  v5 = *a1;
  v6 = a1[1];
  v9 = a1 + 2;
  for ( i = 0; i < a3; ++i )
  {
    v5 = (v5 + 1);
    v7 = v9[v5];
    v6 = (v6 + v7);
    v8 = v9[v6];
    v9[v5] = v8;
    v9[v6] = v7;
    *(i + a2) ^= LOBYTE(v9[(v7 + v8)]);//对输入字符串做异或操作
  }
  *a1 = v5;
  result = a1;
  a1[1] = v6;
  return result;
}

Tab查看 *(i + a2) ^= LOBYTE(v9[(v7 + v8)]);的汇编代码如下。
在这里插入图片描述
不难看出esi里存放的是输入的字符,edx里存放的是待异或的数据,所以要想得到输入字符串与什么做了异或,我们只需要利用动态调试在“xor edx, esi”处下断点,统计edx寄存器里的值就行。

0x10,0x59,0x9C,0x92,0x06,0x22,0xCF,0xA5,
0x72,0x1E,0x45,0x6A,0x06,0xCB,0x08,0xC3,
0xE4,0x49,0x5A,0x63,0x0C,0xDF,0xF6,0x5F,
0x08,0x28,0xBD,0xE2,0x10,0x15,0x1F,0x6E,
0xAA,0x5A,0xCA,0xEC,0x80,0xAF,0x9B,0x16,
0xBB,0x3D,0x13,0x2F,0x6A,0xA4,0xC7,0x2E,
0xBC,0x4B,0x60,0x9A,0xAF,0xE9,0xCE,0xDA,
0x67,0x39,0xBA,0x3B,0x85,0xEB,0xD2,0x6B,
0xAB,0x06,0x6B,0x10,0x57,0x2C,0x88,0x70,
0xF7,0x4F,0xAA,0x7F,0x12,0x47,0xD6,0xDE,
0x74,0xB2,0x1D,0xA4,0xD7,0x76,0x9A,0xE0,

看最后一个函数sub_4008FA;

_DWORD *__fastcall sub_4008FA(__int64 a1, int a2, const char *a3, _DWORD *a4)
{
  int v4; // eax
  int v5; // eax
  unsigned __int8 v6; // al
  int v7; // eax
  unsigned __int8 v8; // al
  int v9; // eax
  int v10; // edx
  _DWORD *result; // rax
  char v13; // [rsp+2Dh] [rbp-13h]
  unsigned __int8 v14; // [rsp+2Eh] [rbp-12h]
  unsigned __int8 v15; // [rsp+2Fh] [rbp-11h]
  int v16; // [rsp+30h] [rbp-10h]
  int v17; // [rsp+34h] [rbp-Ch]

  v16 = 0;
  v17 = 0;
  while ( v17 < a2 )
  {
    v4 = v17++;
    v13 = *(v4 + a1);
    if ( v17 >= a2 )
    {
      v6 = 0;
    }
    else
    {
      v5 = v17++;
      v6 = *(v5 + a1);
    }
    v14 = v6;
    if ( v17 >= a2 )
    {
      v8 = 0;
    }
    else
    {
      v7 = v17++;
      v8 = *(v7 + a1);
    }
    v15 = v8;
    a3[v16] = ((v13 >> 2) & 0x3F) + '=';
    a3[v16 + 1] = (((v14 >> 4) | (16 * v13)) & 0x3F) + 61;
    a3[v16 + 2] = (((v8 >> 6) | (4 * v14)) & 0x3F) + 61;
    v9 = v16 + 3;
    v16 += 4;
    a3[v9] = (v15 & 0x3F) + 61;
  }
  if ( a2 % 3 == 1 )
  {
    a3[--v16] = 61;
  }
  else if ( a2 % 3 != 2 )
  {
    goto LABEL_15;
  }
  a3[v16 - 1] = 61;
LABEL_15:
  v10 = strlen(a3);
  result = a4;
  *a4 = v10;
  return result;
}

上述代码简化之后如下:

_DWORD *__fastcall sub_4008FA(__int64 a1, int a2, const char *a3, _DWORD *a4)
{
	int v4; // eax
	int v5; // eax
	unsigned __int8 v6; // al
	int v7; // eax
	unsigned __int8 v8; // al
	int v9; // eax
	int v10; // edx
	_DWORD *result; // rax
	char v13; // [rsp+2Dh] [rbp-13h]
	unsigned __int8 v14; // [rsp+2Eh] [rbp-12h]
	unsigned __int8 v15; // [rsp+2Fh] [rbp-11h]
	int v16; // [rsp+30h] [rbp-10h]
	int v17; // [rsp+34h] [rbp-Ch]

	v16 = 0;
	v17 = 0;
	while (v17 < a2)
	{
		v13 = a1[v17++];
		v14 = a1[v17++];
		v15 = a1[v17++];//取三个字符

		a3[v16]     = ((          v13 >> 2            ) & 0x3F) + 61;
		a3[v16 + 1] = ((   (v14 >> 4) | ( v13 << 4)   ) & 0x3F) + 61;
		a3[v16 + 2] = ((   (v15 >> 6) | ( v14 << 2)   ) & 0x3F) + 61;
		a3[v16 + 3] =            (v15 & 0x3F)                   + 61;
		v16 += 4;
	}

	//等号填充
	if (a2 % 3 == 1)
	{
		a3[--v16] = 61;
	}
	else if (a2 % 3 != 2)
	{
		goto LABEL_15;
	}
	a3[v16 - 1] = 61;
LABEL_15:
	v10 = strlen(a3);
	result = a4;
	*a4 = v10;
	return result;
}

最后是跟byte_602080作比较
在这里插入图片描述
把byte_602080变成16进制后整理如下:

 0x5a, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44, 
 0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56,
 0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79, 
 0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67, 
 0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70, 
 0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C, 
 0x76, 0x5A, 0x45, 0x3D

下面我们就可以写exp了:

data=[0x5a, 0x60, 0x54, 0x7A, 0x7A, 0x54, 0x72, 0x44,0x7C, 0x66, 0x51, 0x50, 0x5B, 0x5F, 0x56, 0x56,0x4C, 0x7C, 0x79, 0x6E, 0x65, 0x55, 0x52, 0x79,0x55, 0x6D, 0x46, 0x6B, 0x6C, 0x56, 0x4A, 0x67,0x4C, 0x61, 0x73, 0x4A, 0x72, 0x6F, 0x5A, 0x70,0x48, 0x52, 0x78, 0x49, 0x55, 0x6C, 0x48, 0x5C,0x76, 0x5A, 0x45, 0x3D]
flag=''
for i in range(0,len(data),4):
	flag+=chr((((data[i]-0x3D)&0x3F)<<2)|(((data[i+1]-0x3D)&0x30)>>4))
	flag+=chr((((data[i+1]-0x3D)&0x0F)<<4)|(((data[i+2]-0x3D)&0x3C)>>2))
	flag+=chr(((data[i+3]-0x3D)&0x3F)|((data[i+2]-0x3D)&0x03)<<6)
j=0
l=[0x10,0x59,0x9C,0x92,0x06,0x22,0xCF,0xA5,0x72,0x1E,0x45,0x6A,0x06,0xCB,0x08,0xC3,0xE4,0x49,0x5A,0x63,0x0C,0xDF,0xF6,0x5F,0x08,0x28,0xBD,0xE2,0x10,0x15,0x1F,0x6E,0xAA,0x5A,0xCA,0xEC,0x80,0xAF,0x9B,0x16,0xBB,0x3D,0x13,0x2F,0x6A,0xA4,0xC7,0x2E,0xBC,0x4B,0x60,0x9A,0xAF,0xE9,0xCE,0xDA,0x67,0x39,0xBA,0x3B,0x85,0xEB,0xD2,0x6B,0xAB,0x06,0x6B,0x10,0x57,0x2C,0x88,0x70,0xF7,0x4F,0xAA,0x7F,0x12,0x47,0xD6,0xDE,0x74,0xB2,0x1D,0xA4,0xD7,0x76,0x9A,0xE0]
a=list(flag)
flag=''
for i in a:
	flag+=chr(ord(a[j])^l[j])
	j+=1
print(flag)

在这里插入图片描述

flag{e10adc3949ba59abbe56e057f20f883e}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
CTFd是一个用于举办和参加CTF(Capture The Flag)安全竞赛的平台。根据引用\[1\]和引用\[2\]的内容,你可以按照以下步骤在Ubuntu上搭建CTFd平台: 1. 首先,确保你已经安装了虚拟机并配置好了Ubuntu系统。具体的安装和配置步骤可以参考相关的教程。 2. 配置阿里云镜像下载源文件。这可以加快软件包的下载速度。你可以按照引用\[1\]中的指导进行配置。 3. 进入CTFd目录。在终端中使用cd命令进入CTFd的目录。 4. 使用gunicorn工具配置CTFd。根据引用\[2\]和引用\[3\]的内容,你可以使用以下命令配置gunicorn工具: ``` gunicorn --bind 0.0.0.0:8000 -w 5 "CTFd:create_app()" ``` 5. 如果你希望在重启电脑后再次运行CTFd平台,确保以root权限运行。在Ubuntu终端中使用sudo命令运行上述命令。 这样,你就可以在Ubuntu上成功搭建CTFd平台了。请注意,这只是一个简单的搭建过程,具体的配置和使用方法可能会有所不同,你可以参考相关的文档和教程进行更详细的了解和操作。 #### 引用[.reference_title] - *1* *2* *3* [基于Ubuntu搭建CTFd平台(全网最全)](https://blog.csdn.net/qq_25953411/article/details/127489944)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Em0s_Er1t

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值