1.Nftables CVE-2023-0179提权利用
2.求c++编译一个简单的源码计算程序(四则运算)。
Nftables CVE-2023-0179提权利用
在分析了CVE---Nftables整型溢出漏洞的源码成因之后,本文接下来将深入探讨如何利用这一漏洞。源码首先,源码了解到payload_eval_copy_vlan函数中存在整型溢出问题,源码这使得我们可以将vlan头部结构拷贝至寄存器(NFT_REG_-NFT_REG_)中,源码android完整源码下载而该变量位于栈上,源码因此可以覆盖到栈上的源码其他变量。
然而,源码观察源码后发现regs变量无法直接覆盖到返回地址。源码进一步分析后发现,源码jumpstack变量位于regs变量下方,源码推广记录源码因此可以利用整型溢出覆盖到jumpstack变量。源码接下来,源码我们关注nft_jumpstack结构体,源码该结构体在nft_do_chain函数中起作用,当状态寄存器被设置为JUMP条件时,程序将跳转至其他chain进行处理。保存完当前chain状态后,程序将跳转至目的chain,即存储在regs.verdict.chain中。
还原chain的过程涉及通过递减stackptr来取出存储在jumpstack变量中的chain、rule、ucos源码keillastrule,然后进行rule的解析。需要注意的是,在遍历rule时,循环条件为rule < lastrule。因此,在伪造lastrule时,需要确保其值大于rule,否则无法进入循环内部。
接着,观察nft_rule_dp结构体,git阅读源码发现其中包含用于存储nft_expr结构体指针的八个字节。通过篡改该指针,可以劫持程序流程。为了解决这一问题,作者从ctfiot.com/.html中学习了一个技巧:使用ptype /o struct xxx显示结构体信息与偏移。
构建流程如下:通过漏洞溢出至nft_jumpstack结构体,并修改rule变量指向可控内容的地址。同时,将lastrule值篡改为大于rule的值。接下来,在可控内容中伪造一个nft_rule_dp结构体,云巅源码篡改第一个八个字节(填充位)和第二个八个字节(函数表指针)的值,使其分别指向可控内容的地址。之后,在该地址处伪造nft_expr,将ops变量指向所需执行的函数。
分析后得知,通过上述手段可以实现程序流程的劫持。接下来,需要考虑如何伪造上述结构体。在nft_payload_copy_vlan函数中,漏洞点在于将vlan头数据复制到寄存器中,由于vlan头地址低于寄存器地址,导致在复制完vlan头后会覆盖寄存器值。这里,我们可以通过控制NFT_REG_的值来实现对jumpstack结构体的篡改。
由于可以控制regs变量,首先泄露regs地址,然后在此基础上伪造rule并重新指向jumpstack。这里采用将last_rule设置为函数地址的技巧,以节省八个字节的空间。然而,仅控制八个字节的函数指针不足以构造完整的ROP链,因此需要使用栈迁移。栈迁移的目标是控制一段内存,选择regs作为目标。
在寻找合适的栈迁移gadget时,作者使用了vmlinux-to-elf工具提取符号表,然后利用ropper工具搜索gadget。但尝试后发现大部分gadget不可用。作者最后尝试使用objdump工具提取gadget,并通过搜索add rsp.*等指令找到栈迁移的gadget。进一步计算栈顶与regs函数地址的差值,找到相应的栈迁移gadget。
在提权方面,作者通过覆盖modprobe_path实现提权。选择合适的gadget将rdi设置为modprobe_path,rax设置为覆盖后的路径。然而,在返回到nf_hook_slow函数时,由于设置了rax值导致状态码无法正确设置,程序跳转至NF_DROP分支,导致内核异常。最终,作者在rbp中找到了一个do_softirq函数,并尝试将其作为返回地址。运行后发现程序能够正常返回到用户态。
最后,验证新用户是否已写入至/etc/passwd文件中,完成提权过程。完整exploit代码可参考github.com/h0pe-ay/Vuln.../poc.c。相关资料参考链接包括github.com/TurtleARM/CV...
求c++编译一个简单的计算程序(四则运算)。
//正解代码很长很复杂,也许对新人来说太难了。
//此程序可以运算+、-、*、/、乘方(^)、求余数(%),也可以出现( )规定优先级。
//按Ctrl+C退出。
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <math.h>
typedef enum BinOpr
{
OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW, OP_NON
} BinOpr;
static struct { int left, right; } binop_prio[] =
{
{ 6, 6}, { 6, 6}, { 7, 7}, { 7, 7}, { 7, 7}, { ,9},
};
#define UNARY_PRIO 8
static BinOpr get_binop(const char **s)
{
switch (**s)
{
case '+': ++*s; return OP_ADD;
case '-': ++*s; return OP_SUB;
case '*': ++*s; return OP_MUL;
case '/': ++*s; return OP_DIV;
case '%': ++*s; return OP_MOD;
case '^': ++*s; return OP_POW;
default: return OP_NON;
}
}
static double doexpr(int op, double a, double b)
{
switch (op)
{
case OP_ADD: return a+b;
case OP_SUB: return a-b;
case OP_MUL: return a*b;
case OP_DIV: return a/b;
case OP_MOD: return a-floor(a/b)*b;
case OP_POW: return pow(a, b);
default: return 0;
}
}
typedef struct ExprContext
{
jmp_buf jbuf;
const char *errmsg, *s;
BinOpr op;
} Expr;
static double error(Expr *e, const char *msg)
{
e->errmsg = msg;
longjmp(e->jbuf, 1);
}
static double expr(Expr *e, int limit)
{
double n;
BinOpr op;
if (*e->s == '-')
{
++e->s;
n = -expr(e, UNARY_PRIO);
}
else if (*e->s == '(')
{
++e->s;
n = expr(e, 0);
if (*e->s++ != ')') error(e, "')' expected");
}
else {
const char *s = e->s;
n = strtod(s, (char**)&e->s);
if (e->s == s) error(e, "'number' expected");
}
op = get_binop(&e->s);
while (op != OP_NON && binop_prio[op].left > limit)
{
n = doexpr(op, n, expr(e, binop_prio[op].right));
op = e->op;
}
e->op = op;
return n;
}
double calc(const char *s, const char **perr)
{
Expr e;
e.s = s;
e.errmsg = NULL;
if (setjmp(e.jbuf) == 0)
{
double n = expr(&e, 0);
if (*e.s != '\n' && *e.s != '\0' && *e.s != '=')
error(&e, "traling chars detected");
return n;
}
if (perr) *perr = e.errmsg;
return 0;
}
int main(void)
{
char buff[BUFSIZ];
while (printf("> "), fgets(buff, BUFSIZ, stdin) != NULL)
{
const char *errmsg = NULL;
double n = calc(buff, &errmsg);
if (errmsg) printf("ERROR: %s\n", errmsg);
else printf("%g\n", n);
}
return 0;
}
//可以把这个程序留下来,等以后再慢慢研究。正解太复杂太复杂了。
//望采纳
------------------------------------------------------------------------------------