I found a solution that uses jQuery and C# code behind pages. Here is the jQuery part:
$.getJSON('_vti_bin/listdata.svc/<NAME OF LIST (FORMATTED FOR LISTDATA.SVC)>', function(data){
$.each(data.d.results, function(index, value){
$.get('<NAME OF LIST>/'+value.Title+"?noredirect=true", function(info){
$.post('<PATH TO filecreator.aspx>, {file: $(info).find('[nodeName="my:memo"]').text()}, function(data){
$('#response').html(data);
});
});
});
});
The html is a simple enough <div id="response"></div>
just meant to receive data.
Here is the filecreator.aspx file:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Text" %>
<script runat="server">
public void Page_Load(object sender, EventArgs e){
int fileSize, nameLength;
string name = "";
byte[] decodedData;
HttpContext c = HttpContext.Current;
string data64 = c.Request["file"];
if(data64==""){
return;
}
byte[] data = Convert.FromBase64String(data64);
using(MemoryStream ms = new MemoryStream(data)){
BinaryReader reader = new BinaryReader(ms);
byte[] header = new byte[16];
header = reader.ReadBytes(header.Length);
fileSize = (int)reader.ReadUInt32();
nameLength = (int)reader.ReadUInt32() * 2;
byte[] fileName = reader.ReadBytes(nameLength);
Encoding enc = Encoding.Unicode;
name = enc.GetString(fileName, 0, nameLength -2);
decodedData = reader.ReadBytes(fileSize);
}
string filePath = Server.MapPath("/temp/"+name);
if(File.Exists(filePath)){
File.Delete(filePath);
}
FileStream fs = new FileStream(filePath, FileMode.CreateNew);
BinaryWriter writer = new BinaryWriter(fs);
writer.Write(decodedData);
writer.Close();
fs.Close();
Response.Write("<a href=\"http://sharept03sb1/services/?download.aspx?file="+name+"\">"+name+"</a><br />");
}
</script>
And finally, this is download.aspx file that starts the download, it's vulnerable right now to lfi, and I'll mark where this should be fixed:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Text" %>
<script runat="server">
public void Page_Load(object sender, EventArgs e){
string name = Request.QueryString["file"]; // HERE: Make sure to prevent lfi via directory traversal :)
FileStream fs = File.Open(Server.MapPath("/temp/"+name), FileMode.Open);
byte[] file = new byte[fs.Length];
fs.Read(file, 0, Convert.ToInt32(fs.Length));
fs.Close();
Response.AddHeader("Content-disposition", "attachment; filename=" + name);
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(file);
Response.End();
}
</script>
A couple tips:
- If you get something talking about code blocks not working, make sure to do some research on PageParserPaths in sharepoint's web.config file. You'll need to change that.
- This only provides donwload links for every attachment in a document library containing infopath forms. You could of course use parameterized queries to filter certain data.