# Pwn学习总结（9）：格式化字符串漏洞 echo

x86_64, Ubuntu 18.04.5 LTS, Kernel 4.15.0-156-generic
glibc 2.27-3ubuntu1.4, libc6-i386-2.27-3ubuntu1.4


https://github.com/bjrjk/pwn-learning/tree/main/format-string/echo


    Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)


int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char s[256]; // [esp+Ch] [ebp-10Ch] BYREF
unsigned int v4; // [esp+10Ch] [ebp-Ch]

setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
do
{
fgets(s, 256, stdin);
printf(s);
}
while ( strcmp(s, "exit\n") );
system("echo Goodbye");
exit(0);
}


      n      The number of characters written so far is stored into the
integer pointed to by the corresponding argument.  That
argument shall be an int *, or variant whose size matches
the (optionally) supplied integer length modifier.  No
argument is converted.  (This specifier is not supported
by the bionic C library.)  The behavior is undefined if
the conversion specification includes any flags, a field
width, or a precision.


       hh     A following integer conversion corresponds to a signed
char or unsigned char argument, or a following n
conversion corresponds to a pointer to a signed char
argument.

h      A following integer conversion corresponds to a short or
unsigned short argument, or a following n conversion
corresponds to a pointer to a short argument.


%n还可以变为%hhn%hn，改为覆写1字节和2字节。

   Format of the format string
The format string is a character string, beginning and ending in
its initial shift state, if any.  The format string is composed
of zero or more directives: ordinary characters (not %), which
are copied unchanged to the output stream; and conversion
specifications, each of which results in fetching zero or more
subsequent arguments.  Each conversion specification is
introduced by the character %, and ends with a conversion
specifier.  In between there may be (in this order) zero or more
flags, an optional minimum field width, an optional precision and
an optional length modifier.

The overall syntax of a conversion specification is:

%[][flags][width][.precision][length modifier]conversion

The arguments must correspond properly (after type promotion)
with the conversion specifier.  By default, the arguments are
used in the order given, where each '*' (see Field width and
Precision below) and each conversion specifier asks for the next
argument (and it is an error if insufficiently many arguments are
given).  One can also specify explicitly which argument is taken,
at each place where an argument is required, by writing "%m"
integer m denotes the position in the argument list of the
desired argument, indexed starting from 1.  Thus,

printf("%*d", width, num);

and

printf("%2*1d", width, num);

are equivalent.  The second style allows repeated references to
the same argument.  The C99 standard does not include the style
using '', which comes from the Single UNIX Specification.  If
the style using '' is used, it must be used throughout for all
conversions taking an argument and all width and precision
arguments, but it may be mixed with "%%" formats, which do not
consume an argument.  There may be no gaps in the numbers of
arguments specified using ''; for example, if arguments 1 and 3
are specified, argument 2 must also be specified somewhere in the
format string.


printf("%d %d\n", x, y);
return 0;
}


0123456789
1234
10 4


answer_echo.py如下：

#!/usr/bin/env python2
from pwn import *
from LibcSearcher import *
from struct import pack
import os, base64, math
context(arch = "i386",os = "linux", log_level = "debug")

p = process('./echo')
elf = ELF('./echo')

printf_got = elf.got['printf']
system_plt = elf.plt['system']