picoCTF - re - Rolling My Own

雖然題目hint給了對(duì)應(yīng)的論文,但其實(shí)看不看問題都不大,本質(zhì)上就是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字節(jié)會(huì)用作運(yùn)算,從input到enc的邏輯梳理后為:



MD5函數(shù)實(shí)際上就是分塊求plain的MD5值再拼接在一起,而fin是enc的一個(gè)hash,簡(jiǎn)單驗(yàn)證后可以發(fā)現(xiàn)就是提取出了每個(gè)MD5塊的特定位置上的字節(jié):

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在最后作匯編碼直接執(zhí)行,且第一個(gè)參數(shù)為函數(shù)指針,函數(shù)體為:

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;
}

這意味我們需要調(diào)用這一函數(shù)并將第一個(gè)參數(shù)改為0x7b3dc26f1,再結(jié)合這一程序的調(diào)用約定,可以寫出shellcode:

    mov rsi, rdi
    mov rdi, 0x7b3dc26f1
    call rsi

利用pwntools的asm可以得到最終的shellcode,剩下的事情只需要爆破MD5就可以了,需要注意的是shellcode只有15字節(jié),所以最后一字節(jié)可以不做限制,最終的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))

最終運(yùn)行結(jié)果為:

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。其實(shí)更有意思的部分是如何由任意leet style的文本的MD5提取出所需要的序列,即這一題的構(gòu)造方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • RE obfu 將輸入的字符串轉(zhuǎn)化為16進(jìn)制后存儲(chǔ)。((input[(i+15) % 16] << 5) & 0x...
    G3n3rous閱讀 1,721評(píng)論 0 2
  • 0x01 codemap 1. 題目描述 2. 題目分析 我們直接看IDA反匯編后的代碼: 經(jīng)過分析我們可以看到,...
    Nevv閱讀 649評(píng)論 0 4
  • 前言: 此文用于記錄做BugkuCTF時(shí)遇到的知識(shí),主要為了鞏固和查閱。 記錄各種知識(shí) 1.stripos(字符串...
    煊奕閱讀 2,021評(píng)論 0 4
  • Bugku-WEB 1、web2 查看源代碼,在url前面加上view-source:,搜索flag即可 2、計(jì)算...
    glotozz閱讀 1,810評(píng)論 0 0
  • 前言 隨著"網(wǎng)絡(luò)空間安全”被批準(zhǔn)為國家一級(jí)學(xué)科,各高校網(wǎng)絡(luò)空間安全學(xué)院如雨后春筍般紛紛成立,但各高校的網(wǎng)絡(luò)安全教育...
    Du1in9閱讀 4,772評(píng)論 8 19

友情鏈接更多精彩內(nèi)容