picoCTF - re - Rolling My Own

虽然题目hint给了对应的论文,但其实看不看问题都不大,本质上就是MD5的爆破。先从程序逻辑开始看:

undefined8 main(void)

{
  size_t len;
  void *enc;
  undefined8 *code;
  long in_FS_OFFSET;
  int i;
  int j;
  int arr [4];
  undefined8 fin;
  undefined8 local_d0;
  char salt [33];
  char input [65];
  char plain [72];
  long canary;
  
  canary = *(long *)(in_FS_OFFSET + 0x28);
  setbuf(stdout,(char *)0x0);
  salt._0_8_ = 0x57456a4d614c7047;
  salt._8_8_ = 0x6b6d6e6e6a4f5670;
  salt._16_8_ = 0x367064656c694752;
  salt._24_8_ = 0x736c787a6563764d;
  salt[32] = '\0';
  arr[0] = 8;
  arr[1] = 2;
  arr[2] = 7;
  arr[3] = 1;
  memset(input + 1,0,0x40);
  memset(plain,0,0x40);
  printf("Password: ");
  fgets(input + 1,0x40,stdin);
  len = strlen(input + 1);
  input[len] = '\0';
  for (i = 0; i < 4; i = i + 1) {
    strncat(plain,input + (long)(i << 2) + 1,4);
    strncat(plain,salt + (i << 3),8);
  }
  enc = malloc(0x40);
  len = strlen(plain);
  MD5(enc,plain,(int)len);
  for (i = 0; i < 4; i = i + 1) {
    for (j = 0; j < 4; j = j + 1) {
      *(undefined *)((long)&fin + (long)(j * 4 + i)) =
           *(undefined *)((long)enc + (long)(arr[j] + j * 0x10 + i));
    }
  }
  code = (undefined8 *)mmap((void *)0x0,0x10,7,0x22,-1,0);
  *code = fin;
  code[1] = local_d0;
  (*(code *)code)(FUN_0010102b);
  free(enc);
  if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

void MD5(void *enc,void *plain,int len)

{
  int blocks;
  uint uVar1;
  int iVar2;
  long in_FS_OFFSET;
  void *plain_block;
  int i;
  int j;
  int blocksize;
  MD5_CTX context;
  uchar enc_block [24];
  long canary;
  
  canary = *(long *)(in_FS_OFFSET + 0x28);
  if (len % 12 == 0) {
    blocks = len / 12;
  }
  else {
    blocks = len / 12 + 1;
  }
  plain_block = plain;
  for (i = 0; i < blocks; i = i + 1) {
    blocksize = 0xc;
    if ((i == blocks + -1) && (len % 12 != 0)) {
      blocksize = blocks % 0xc;
    }
    MD5_Init(&context);
    MD5_Update(&context,plain_block,(long)blocksize);
    plain_block = (void *)((long)plain_block + (long)blocksize);
    MD5_Final(enc_block,&context);
    for (j = 0; j < 16; j = j + 1) {
      iVar2 = i * 16 + j;
      uVar1 = (uint)(iVar2 >> 31) >> 26;
      *(uchar *)((long)(int)((iVar2 + uVar1 & 63) - uVar1) + (long)enc) = enc_block[j];
    }
  }
  if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

input只有前16字节会用作运算,从input到enc的逻辑梳理后为:



MD5函数实际上就是分块求plain的MD5值再拼接在一起,而fin是enc的一个hash,简单验证后可以发现就是提取出了每个MD5块的特定位置上的字节:

enc = [i for i in range(64)]
fin = [0] * 16
arr = [8, 2, 7, 1]

for i in range(4):
    for j in range(4):
        fin[i + 4 * j] = enc[i + 16 * j + arr[j]]
# fin = [8, 9, 10, 11, 18, 19, 20, 21, 39, 40, 41, 42, 49, 50, 51, 52]

而fin在最后作汇编码直接执行,且第一个参数为函数指针,函数体为:

void FUN_0010102b(long key)

{
  FILE *__stream;
  long in_FS_OFFSET;
  char local_98 [136];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  if (key == 0x7b3dc26f1) {
    __stream = fopen("flag","r");
    if (__stream == (FILE *)0x0) {
      puts("Flag file not found. Contact an admin.");
                    /* WARNING: Subroutine does not return */
      exit(1);
    }
    fgets(local_98,0x80,__stream);
    puts(local_98);
  }
  else {
    puts("Hmmmmmm... not quite");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return;
}

这意味我们需要调用这一函数并将第一个参数改为0x7b3dc26f1,再结合这一程序的调用约定,可以写出shellcode:

    mov rsi, rdi
    mov rdi, 0x7b3dc26f1
    call rsi

利用pwntools的asm可以得到最终的shellcode,剩下的事情只需要爆破MD5就可以了,需要注意的是shellcode只有15字节,所以最后一字节可以不做限制,最终的exp为:

from hashlib import md5
from pwn import *

enc = [i for i in range(64)]
fin = [0] * 16
arr = [8, 2, 7, 1]

for i in range(4):
    for j in range(4):
        fin[i + 4 * j] = enc[i + 16 * j + arr[j]]

for i in range(len(fin)):
    fin[i] %= 16
# fin = [8, 9, 10, 11, 2, 3, 4, 5, 7, 8, 9, 10, 1, 2, 3, 4]

context.arch = "amd64"
shellcode = asm(
    """
    mov rsi, rdi
    mov rdi, 0x7b3dc26f1
    call rsi
    """
)
shellcode += b"\x90"
salt = [b"GpLaMjEW", b"pVOjnnmk", b"RGiledp6", b"Mvcezxls"]
for i in range(4):
    for a in range(33, 123, 1):
        for b in range(33, 123, 1):
            for c in range(33, 123, 1):
                for d in range(33, 123, 1):
                    enc_block = (
                        a.to_bytes(1, "big")
                        + b.to_bytes(1, "big")
                        + c.to_bytes(1, "big")
                        + d.to_bytes(1, "big")
                    )
                    md5_sum = md5(enc_block + salt[i]).digest()
                    match = 0
                    # print(enc_block)
                    if i != 3:
                        for idx, pos in enumerate(fin[i * 4 : i * 4 + 4]):
                            if md5_sum[pos] == shellcode[idx + i * 4]:
                                match += 1
                        if match == 4:
                            print("The {}: {}".format(i, enc_block))
                    else:
                        for idx, pos in enumerate(fin[i * 4 : i * 4 + 3]):
                            if md5_sum[pos] == shellcode[idx + i * 4]:
                                match += 1
                        if match == 3:
                            print("The {}: {}".format(i, enc_block))

最终运行结果为:

zyd@ubuntu:~$ python test.py 
The 0: b'D1v1'
The 1: b'd3An'
The 2: b'dC0n'
The 3: b'\\rpB'
The 3: b'qu3r'

exp只选择了ascii的可打印字符作为字典集,最终D1v1d3AndC0nqu3rD1v1d3AndC0n\rpB就是需要的input。其实更有意思的部分是如何由任意leet style的文本的MD5提取出所需要的序列,即这一题的构造方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • RE obfu 将输入的字符串转化为16进制后存储。((input[(i+15) % 16] << 5) & 0x...
    G3n3rous阅读 1,548评论 0 2
  • 0x01 codemap 1. 题目描述 2. 题目分析 我们直接看IDA反汇编后的代码: 经过分析我们可以看到,...
    Nevv阅读 615评论 0 4
  • 前言: 此文用于记录做BugkuCTF时遇到的知识,主要为了巩固和查阅。 记录各种知识 1.stripos(字符串...
    煊奕阅读 1,931评论 0 4
  • Bugku-WEB 1、web2 查看源代码,在url前面加上view-source:,搜索flag即可 2、计算...
    glotozz阅读 1,352评论 0 0
  • 前言 随着"网络空间安全”被批准为国家一级学科,各高校网络空间安全学院如雨后春笋般纷纷成立,但各高校的网络安全教育...
    Du1in9阅读 4,409评论 8 19